diff --git a/.github/workflows/platform-backend-ci.yml b/.github/workflows/platform-backend-ci.yml index 330646168e..3492a00ea9 100644 --- a/.github/workflows/platform-backend-ci.yml +++ b/.github/workflows/platform-backend-ci.yml @@ -50,6 +50,23 @@ jobs: env: RABBITMQ_DEFAULT_USER: ${{ env.RABBITMQ_DEFAULT_USER }} RABBITMQ_DEFAULT_PASS: ${{ env.RABBITMQ_DEFAULT_PASS }} + clamav: + image: clamav/clamav-debian:latest + ports: + - 3310:3310 + env: + CLAMAV_NO_FRESHCLAMD: false + CLAMD_CONF_StreamMaxLength: 50M + CLAMD_CONF_MaxFileSize: 100M + CLAMD_CONF_MaxScanSize: 100M + CLAMD_CONF_MaxThreads: 4 + CLAMD_CONF_ReadTimeout: 300 + options: >- + --health-cmd "clamdscan --version || exit 1" + --health-interval 30s + --health-timeout 10s + --health-retries 5 + --health-start-period 180s steps: - name: Checkout repository @@ -131,6 +148,35 @@ jobs: # outputs: # DB_URL, API_URL, GRAPHQL_URL, ANON_KEY, SERVICE_ROLE_KEY, JWT_SECRET + - name: Wait for ClamAV to be ready + run: | + echo "Waiting for ClamAV daemon to start..." + max_attempts=60 + attempt=0 + + until nc -z localhost 3310 || [ $attempt -eq $max_attempts ]; do + echo "ClamAV is unavailable - sleeping (attempt $((attempt+1))/$max_attempts)" + sleep 5 + attempt=$((attempt+1)) + done + + if [ $attempt -eq $max_attempts ]; then + echo "ClamAV failed to start after $((max_attempts*5)) seconds" + echo "Checking ClamAV service logs..." + docker logs $(docker ps -q --filter "ancestor=clamav/clamav-debian:latest") 2>&1 | tail -50 || echo "No ClamAV container found" + exit 1 + fi + + echo "ClamAV is ready!" + + # Verify ClamAV is responsive + echo "Testing ClamAV connection..." + timeout 10 bash -c 'echo "PING" | nc localhost 3310' || { + echo "ClamAV is not responding to PING" + docker logs $(docker ps -q --filter "ancestor=clamav/clamav-debian:latest") 2>&1 | tail -50 || echo "No ClamAV container found" + exit 1 + } + - name: Run Database Migrations run: poetry run prisma migrate dev --name updates env: @@ -144,9 +190,9 @@ jobs: - name: Run pytest with coverage run: | if [[ "${{ runner.debug }}" == "1" ]]; then - poetry run pytest -s -vv -o log_cli=true -o log_cli_level=DEBUG test + poetry run pytest -s -vv -o log_cli=true -o log_cli_level=DEBUG else - poetry run pytest -s -vv test + poetry run pytest -s -vv fi if: success() || (failure() && steps.lint.outcome == 'failure') env: @@ -159,6 +205,7 @@ jobs: REDIS_HOST: "localhost" REDIS_PORT: "6379" REDIS_PASSWORD: "testpassword" + ENCRYPTION_KEY: "dvziYgz0KSK8FENhju0ZYi8-fRTfAdlz6YLhdB_jhNw=" # DO NOT USE IN PRODUCTION!! env: CI: true diff --git a/.github/workflows/platform-frontend-ci.yml b/.github/workflows/platform-frontend-ci.yml index 486a7d07f5..96cd7ddedd 100644 --- a/.github/workflows/platform-frontend-ci.yml +++ b/.github/workflows/platform-frontend-ci.yml @@ -55,12 +55,37 @@ jobs: - name: Install dependencies run: pnpm install --frozen-lockfile - - name: Generate API client - run: pnpm generate:api-client - - name: Run tsc check run: pnpm type-check + chromatic: + runs-on: ubuntu-latest + # Only run on dev branch pushes or PRs targeting dev + if: github.ref == 'refs/heads/dev' || github.base_ref == 'dev' + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: "21" + + - name: Enable corepack + run: corepack enable + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run Chromatic + uses: chromaui/action@latest + with: + projectToken: chpt_9e7c1a76478c9c8 + onlyChanged: true + workingDir: autogpt_platform/frontend + token: ${{ secrets.GITHUB_TOKEN }} + test: runs-on: ubuntu-latest strategy: diff --git a/.gitignore b/.gitignore index b6c2fdc035..1067dd921c 100644 --- a/.gitignore +++ b/.gitignore @@ -177,6 +177,3 @@ autogpt_platform/backend/settings.py *.ign.* .test-contents .claude/settings.local.json - -# Auto generated client -autogpt_platform/frontend/src/api/__generated__ diff --git a/autogpt_platform/CLAUDE.md b/autogpt_platform/CLAUDE.md index 93f64e1eb3..7ae65a658c 100644 --- a/autogpt_platform/CLAUDE.md +++ b/autogpt_platform/CLAUDE.md @@ -19,7 +19,7 @@ cd backend && poetry install # Run database migrations poetry run prisma migrate dev -# Start all services (database, redis, rabbitmq) +# Start all services (database, redis, rabbitmq, clamav) docker compose up -d # Run the backend server @@ -92,6 +92,7 @@ npm run type-check 2. **Blocks**: Reusable components in `/backend/blocks/` that perform specific tasks 3. **Integrations**: OAuth and API connections stored per user 4. **Store**: Marketplace for sharing agent templates +5. **Virus Scanning**: ClamAV integration for file upload security ### Testing Approach - Backend uses pytest with snapshot testing for API responses diff --git a/autogpt_platform/backend/.env.example b/autogpt_platform/backend/.env.example index 18343d7725..395248f7ed 100644 --- a/autogpt_platform/backend/.env.example +++ b/autogpt_platform/backend/.env.example @@ -55,9 +55,9 @@ RABBITMQ_DEFAULT_PASS=k0VMxyIJF9S35f3x2uaw5IWAl6Y536O7 ## GCS bucket is required for marketplace and library functionality MEDIA_GCS_BUCKET_NAME= -## For local development, you may need to set FRONTEND_BASE_URL for the OAuth flow +## For local development, you may need to set NEXT_PUBLIC_FRONTEND_BASE_URL for the OAuth flow ## for integrations to work. Defaults to the value of PLATFORM_BASE_URL if not set. -# FRONTEND_BASE_URL=http://localhost:3000 +# NEXT_PUBLIC_FRONTEND_BASE_URL=http://localhost:3000 ## PLATFORM_BASE_URL must be set to a *publicly accessible* URL pointing to your backend ## to use the platform's webhook-related functionality. diff --git a/autogpt_platform/backend/backend/blocks/__init__.py b/autogpt_platform/backend/backend/blocks/__init__.py index 0f57d531f7..4291c4f358 100644 --- a/autogpt_platform/backend/backend/blocks/__init__.py +++ b/autogpt_platform/backend/backend/blocks/__init__.py @@ -20,7 +20,7 @@ def load_all_blocks() -> dict[str, type["Block"]]: modules = [ str(f.relative_to(current_dir))[:-3].replace(os.path.sep, ".") for f in current_dir.rglob("*.py") - if f.is_file() and f.name != "__init__.py" + if f.is_file() and f.name != "__init__.py" and not f.name.startswith("test_") ] for module in modules: if not re.match("^[a-z0-9_.]+$", module): diff --git a/autogpt_platform/backend/backend/blocks/agent.py b/autogpt_platform/backend/backend/blocks/agent.py index c25d99458d..69d3c85269 100644 --- a/autogpt_platform/backend/backend/blocks/agent.py +++ b/autogpt_platform/backend/backend/blocks/agent.py @@ -2,6 +2,8 @@ import asyncio import logging from typing import Any, Optional +from pydantic import JsonValue + from backend.data.block import ( Block, BlockCategory, @@ -12,7 +14,7 @@ from backend.data.block import ( get_block, ) from backend.data.execution import ExecutionStatus -from backend.data.model import CredentialsMetaInput, SchemaField +from backend.data.model import SchemaField from backend.util import json logger = logging.getLogger(__name__) @@ -28,9 +30,9 @@ class AgentExecutorBlock(Block): input_schema: dict = SchemaField(description="Input schema for the graph") output_schema: dict = SchemaField(description="Output schema for the graph") - node_credentials_input_map: Optional[ - dict[str, dict[str, CredentialsMetaInput]] - ] = SchemaField(default=None, hidden=True) + nodes_input_masks: Optional[dict[str, dict[str, JsonValue]]] = SchemaField( + default=None, hidden=True + ) @classmethod def get_input_schema(cls, data: BlockInput) -> dict[str, Any]: @@ -71,7 +73,7 @@ class AgentExecutorBlock(Block): graph_version=input_data.graph_version, user_id=input_data.user_id, inputs=input_data.inputs, - node_credentials_input_map=input_data.node_credentials_input_map, + nodes_input_masks=input_data.nodes_input_masks, use_db_query=False, ) diff --git a/autogpt_platform/backend/backend/blocks/ai_shortform_video_block.py b/autogpt_platform/backend/backend/blocks/ai_shortform_video_block.py index c3c4e36472..fe13687970 100644 --- a/autogpt_platform/backend/backend/blocks/ai_shortform_video_block.py +++ b/autogpt_platform/backend/backend/blocks/ai_shortform_video_block.py @@ -53,6 +53,7 @@ class AudioTrack(str, Enum): REFRESHER = ("Refresher",) TOURIST = ("Tourist",) TWIN_TYCHES = ("Twin Tyches",) + DONT_STOP_ME_ABSTRACT_FUTURE_BASS = ("Dont Stop Me Abstract Future Bass",) @property def audio_url(self): @@ -78,6 +79,7 @@ class AudioTrack(str, Enum): AudioTrack.REFRESHER: "https://cdn.tfrv.xyz/audio/refresher.mp3", AudioTrack.TOURIST: "https://cdn.tfrv.xyz/audio/tourist.mp3", AudioTrack.TWIN_TYCHES: "https://cdn.tfrv.xyz/audio/twin-tynches.mp3", + AudioTrack.DONT_STOP_ME_ABSTRACT_FUTURE_BASS: "https://cdn.revid.ai/audio/_dont-stop-me-abstract-future-bass.mp3", } return audio_urls[self] @@ -105,6 +107,7 @@ class GenerationPreset(str, Enum): MOVIE = ("Movie",) STYLIZED_ILLUSTRATION = ("Stylized Illustration",) MANGA = ("Manga",) + DEFAULT = ("DEFAULT",) class Voice(str, Enum): @@ -114,6 +117,7 @@ class Voice(str, Enum): JESSICA = "Jessica" CHARLOTTE = "Charlotte" CALLUM = "Callum" + EVA = "Eva" @property def voice_id(self): @@ -124,6 +128,7 @@ class Voice(str, Enum): Voice.JESSICA: "cgSgspJ2msm6clMCkdW9", Voice.CHARLOTTE: "XB0fDUnXU5powFXDhCwa", Voice.CALLUM: "N2lVS1w4EtoT3dr4eOWO", + Voice.EVA: "FGY2WhTYpPnrIDTdsKH5", } return voice_id_map[self] @@ -141,6 +146,8 @@ logger = logging.getLogger(__name__) class AIShortformVideoCreatorBlock(Block): + """Creates a short‑form text‑to‑video clip using stock or AI imagery.""" + class Input(BlockSchema): credentials: CredentialsMetaInput[ Literal[ProviderName.REVID], Literal["api_key"] @@ -184,40 +191,8 @@ class AIShortformVideoCreatorBlock(Block): video_url: str = SchemaField(description="The URL of the created video") error: str = SchemaField(description="Error message if the request failed") - def __init__(self): - super().__init__( - id="361697fb-0c4f-4feb-aed3-8320c88c771b", - description="Creates a shortform video using revid.ai", - categories={BlockCategory.SOCIAL, BlockCategory.AI}, - input_schema=AIShortformVideoCreatorBlock.Input, - output_schema=AIShortformVideoCreatorBlock.Output, - test_input={ - "credentials": TEST_CREDENTIALS_INPUT, - "script": "[close-up of a cat] Meow!", - "ratio": "9 / 16", - "resolution": "720p", - "frame_rate": 60, - "generation_preset": GenerationPreset.LEONARDO, - "background_music": AudioTrack.HIGHWAY_NOCTURNE, - "voice": Voice.LILY, - "video_style": VisualMediaType.STOCK_VIDEOS, - }, - test_output=( - "video_url", - "https://example.com/video.mp4", - ), - test_mock={ - "create_webhook": lambda: ( - "test_uuid", - "https://webhook.site/test_uuid", - ), - "create_video": lambda api_key, payload: {"pid": "test_pid"}, - "wait_for_video": lambda api_key, pid, webhook_token, max_wait_time=1000: "https://example.com/video.mp4", - }, - test_credentials=TEST_CREDENTIALS, - ) - - async def create_webhook(self): + async def create_webhook(self) -> tuple[str, str]: + """Create a new webhook URL for receiving notifications.""" url = "https://webhook.site/token" headers = {"Accept": "application/json", "Content-Type": "application/json"} response = await Requests().post(url, headers=headers) @@ -225,6 +200,7 @@ class AIShortformVideoCreatorBlock(Block): return webhook_data["uuid"], f"https://webhook.site/{webhook_data['uuid']}" async def create_video(self, api_key: SecretStr, payload: dict) -> dict: + """Create a video using the Revid API.""" url = "https://www.revid.ai/api/public/v2/render" headers = {"key": api_key.get_secret_value()} response = await Requests().post(url, json=payload, headers=headers) @@ -234,6 +210,7 @@ class AIShortformVideoCreatorBlock(Block): return response.json() async def check_video_status(self, api_key: SecretStr, pid: str) -> dict: + """Check the status of a video creation job.""" url = f"https://www.revid.ai/api/public/v2/status?pid={pid}" headers = {"key": api_key.get_secret_value()} response = await Requests().get(url, headers=headers) @@ -243,9 +220,9 @@ class AIShortformVideoCreatorBlock(Block): self, api_key: SecretStr, pid: str, - webhook_token: str, max_wait_time: int = 1000, ) -> str: + """Wait for video creation to complete and return the video URL.""" start_time = time.time() while time.time() - start_time < max_wait_time: status = await self.check_video_status(api_key, pid) @@ -266,6 +243,40 @@ class AIShortformVideoCreatorBlock(Block): logger.error("Video creation timed out") raise TimeoutError("Video creation timed out") + def __init__(self): + super().__init__( + id="361697fb-0c4f-4feb-aed3-8320c88c771b", + description="Creates a shortform video using revid.ai", + categories={BlockCategory.SOCIAL, BlockCategory.AI}, + input_schema=AIShortformVideoCreatorBlock.Input, + output_schema=AIShortformVideoCreatorBlock.Output, + test_input={ + "credentials": TEST_CREDENTIALS_INPUT, + "script": "[close-up of a cat] Meow!", + "ratio": "9 / 16", + "resolution": "720p", + "frame_rate": 60, + "generation_preset": GenerationPreset.LEONARDO, + "background_music": AudioTrack.HIGHWAY_NOCTURNE, + "voice": Voice.LILY, + "video_style": VisualMediaType.STOCK_VIDEOS, + }, + test_output=("video_url", "https://example.com/video.mp4"), + test_mock={ + "create_webhook": lambda *args, **kwargs: ( + "test_uuid", + "https://webhook.site/test_uuid", + ), + "create_video": lambda *args, **kwargs: {"pid": "test_pid"}, + "check_video_status": lambda *args, **kwargs: { + "status": "ready", + "videoUrl": "https://example.com/video.mp4", + }, + "wait_for_video": lambda *args, **kwargs: "https://example.com/video.mp4", + }, + test_credentials=TEST_CREDENTIALS, + ) + async def run( self, input_data: Input, *, credentials: APIKeyCredentials, **kwargs ) -> BlockOutput: @@ -273,20 +284,18 @@ class AIShortformVideoCreatorBlock(Block): webhook_token, webhook_url = await self.create_webhook() logger.debug(f"Webhook URL: {webhook_url}") - audio_url = input_data.background_music.audio_url - payload = { "frameRate": input_data.frame_rate, "resolution": input_data.resolution, "frameDurationMultiplier": 18, - "webhook": webhook_url, + "webhook": None, "creationParams": { "mediaType": input_data.video_style, "captionPresetName": "Wrap 1", "selectedVoice": input_data.voice.voice_id, "hasEnhancedGeneration": True, "generationPreset": input_data.generation_preset.name, - "selectedAudio": input_data.background_music, + "selectedAudio": input_data.background_music.value, "origin": "/create", "inputText": input_data.script, "flowType": "text-to-video", @@ -302,7 +311,7 @@ class AIShortformVideoCreatorBlock(Block): "selectedStoryStyle": {"value": "custom", "label": "Custom"}, "hasToGenerateVideos": input_data.video_style != VisualMediaType.STOCK_VIDEOS, - "audioUrl": audio_url, + "audioUrl": input_data.background_music.audio_url, }, } @@ -319,8 +328,370 @@ class AIShortformVideoCreatorBlock(Block): logger.debug( f"Video created with project ID: {pid}. Waiting for completion..." ) - video_url = await self.wait_for_video( - credentials.api_key, pid, webhook_token - ) + video_url = await self.wait_for_video(credentials.api_key, pid) logger.debug(f"Video ready: {video_url}") yield "video_url", video_url + + +class AIAdMakerVideoCreatorBlock(Block): + """Generates a 30‑second vertical AI advert using optional user‑supplied imagery.""" + + class Input(BlockSchema): + credentials: CredentialsMetaInput[ + Literal[ProviderName.REVID], Literal["api_key"] + ] = CredentialsField( + description="Credentials for Revid.ai API access.", + ) + script: str = SchemaField( + description="Short advertising copy. Line breaks create new scenes.", + placeholder="Introducing Foobar – [show product photo] the gadget that does it all.", + ) + ratio: str = SchemaField(description="Aspect ratio", default="9 / 16") + target_duration: int = SchemaField( + description="Desired length of the ad in seconds.", default=30 + ) + voice: Voice = SchemaField( + description="Narration voice", default=Voice.EVA, placeholder=Voice.EVA + ) + background_music: AudioTrack = SchemaField( + description="Background track", + default=AudioTrack.DONT_STOP_ME_ABSTRACT_FUTURE_BASS, + ) + input_media_urls: list[str] = SchemaField( + description="List of image URLs to feature in the advert.", default=[] + ) + use_only_provided_media: bool = SchemaField( + description="Restrict visuals to supplied images only.", default=True + ) + + class Output(BlockSchema): + video_url: str = SchemaField(description="URL of the finished advert") + error: str = SchemaField(description="Error message on failure") + + async def create_webhook(self) -> tuple[str, str]: + """Create a new webhook URL for receiving notifications.""" + url = "https://webhook.site/token" + headers = {"Accept": "application/json", "Content-Type": "application/json"} + response = await Requests().post(url, headers=headers) + webhook_data = response.json() + return webhook_data["uuid"], f"https://webhook.site/{webhook_data['uuid']}" + + async def create_video(self, api_key: SecretStr, payload: dict) -> dict: + """Create a video using the Revid API.""" + url = "https://www.revid.ai/api/public/v2/render" + headers = {"key": api_key.get_secret_value()} + response = await Requests().post(url, json=payload, headers=headers) + logger.debug( + f"API Response Status Code: {response.status}, Content: {response.text}" + ) + return response.json() + + async def check_video_status(self, api_key: SecretStr, pid: str) -> dict: + """Check the status of a video creation job.""" + url = f"https://www.revid.ai/api/public/v2/status?pid={pid}" + headers = {"key": api_key.get_secret_value()} + response = await Requests().get(url, headers=headers) + return response.json() + + async def wait_for_video( + self, + api_key: SecretStr, + pid: str, + max_wait_time: int = 1000, + ) -> str: + """Wait for video creation to complete and return the video URL.""" + start_time = time.time() + while time.time() - start_time < max_wait_time: + status = await self.check_video_status(api_key, pid) + logger.debug(f"Video status: {status}") + + if status.get("status") == "ready" and "videoUrl" in status: + return status["videoUrl"] + elif status.get("status") == "error": + error_message = status.get("error", "Unknown error occurred") + logger.error(f"Video creation failed: {error_message}") + raise ValueError(f"Video creation failed: {error_message}") + elif status.get("status") in ["FAILED", "CANCELED"]: + logger.error(f"Video creation failed: {status.get('message')}") + raise ValueError(f"Video creation failed: {status.get('message')}") + + await asyncio.sleep(10) + + logger.error("Video creation timed out") + raise TimeoutError("Video creation timed out") + + def __init__(self): + super().__init__( + id="58bd2a19-115d-4fd1-8ca4-13b9e37fa6a0", + description="Creates an AI‑generated 30‑second advert (text + images)", + categories={BlockCategory.MARKETING, BlockCategory.AI}, + input_schema=AIAdMakerVideoCreatorBlock.Input, + output_schema=AIAdMakerVideoCreatorBlock.Output, + test_input={ + "credentials": TEST_CREDENTIALS_INPUT, + "script": "Test product launch!", + "input_media_urls": [ + "https://cdn.revid.ai/uploads/1747076315114-image.png", + ], + }, + test_output=("video_url", "https://example.com/ad.mp4"), + test_mock={ + "create_webhook": lambda *args, **kwargs: ( + "test_uuid", + "https://webhook.site/test_uuid", + ), + "create_video": lambda *args, **kwargs: {"pid": "test_pid"}, + "check_video_status": lambda *args, **kwargs: { + "status": "ready", + "videoUrl": "https://example.com/ad.mp4", + }, + "wait_for_video": lambda *args, **kwargs: "https://example.com/ad.mp4", + }, + test_credentials=TEST_CREDENTIALS, + ) + + async def run(self, input_data: Input, *, credentials: APIKeyCredentials, **kwargs): + webhook_token, webhook_url = await self.create_webhook() + + payload = { + "webhook": webhook_url, + "creationParams": { + "targetDuration": input_data.target_duration, + "ratio": input_data.ratio, + "mediaType": "aiVideo", + "inputText": input_data.script, + "flowType": "text-to-video", + "slug": "ai-ad-generator", + "slugNew": "", + "isCopiedFrom": False, + "hasToGenerateVoice": True, + "hasToTranscript": False, + "hasToSearchMedia": True, + "hasAvatar": False, + "hasWebsiteRecorder": False, + "hasTextSmallAtBottom": False, + "selectedAudio": input_data.background_music.value, + "selectedVoice": input_data.voice.voice_id, + "selectedAvatar": "https://cdn.revid.ai/avatars/young-woman.mp4", + "selectedAvatarType": "video/mp4", + "websiteToRecord": "", + "hasToGenerateCover": True, + "nbGenerations": 1, + "disableCaptions": False, + "mediaMultiplier": "medium", + "characters": [], + "captionPresetName": "Revid", + "sourceType": "contentScraping", + "selectedStoryStyle": {"value": "custom", "label": "General"}, + "generationPreset": "DEFAULT", + "hasToGenerateMusic": False, + "isOptimizedForChinese": False, + "generationUserPrompt": "", + "enableNsfwFilter": False, + "addStickers": False, + "typeMovingImageAnim": "dynamic", + "hasToGenerateSoundEffects": False, + "forceModelType": "gpt-image-1", + "selectedCharacters": [], + "lang": "", + "voiceSpeed": 1, + "disableAudio": False, + "disableVoice": False, + "useOnlyProvidedMedia": input_data.use_only_provided_media, + "imageGenerationModel": "ultra", + "videoGenerationModel": "pro", + "hasEnhancedGeneration": True, + "hasEnhancedGenerationPro": True, + "inputMedias": [ + {"url": url, "title": "", "type": "image"} + for url in input_data.input_media_urls + ], + "hasToGenerateVideos": True, + "audioUrl": input_data.background_music.audio_url, + "watermark": None, + }, + } + + response = await self.create_video(credentials.api_key, payload) + pid = response.get("pid") + if not pid: + raise RuntimeError("Failed to create video: No project ID returned") + + video_url = await self.wait_for_video(credentials.api_key, pid) + yield "video_url", video_url + + +class AIScreenshotToVideoAdBlock(Block): + """Creates an advert where the supplied screenshot is narrated by an AI avatar.""" + + class Input(BlockSchema): + credentials: CredentialsMetaInput[ + Literal[ProviderName.REVID], Literal["api_key"] + ] = CredentialsField(description="Revid.ai API key") + script: str = SchemaField( + description="Narration that will accompany the screenshot.", + placeholder="Check out these amazing stats!", + ) + screenshot_url: str = SchemaField( + description="Screenshot or image URL to showcase." + ) + ratio: str = SchemaField(default="9 / 16") + target_duration: int = SchemaField(default=30) + voice: Voice = SchemaField(default=Voice.EVA) + background_music: AudioTrack = SchemaField( + default=AudioTrack.DONT_STOP_ME_ABSTRACT_FUTURE_BASS + ) + + class Output(BlockSchema): + video_url: str = SchemaField(description="Rendered video URL") + error: str = SchemaField(description="Error, if encountered") + + async def create_webhook(self) -> tuple[str, str]: + """Create a new webhook URL for receiving notifications.""" + url = "https://webhook.site/token" + headers = {"Accept": "application/json", "Content-Type": "application/json"} + response = await Requests().post(url, headers=headers) + webhook_data = response.json() + return webhook_data["uuid"], f"https://webhook.site/{webhook_data['uuid']}" + + async def create_video(self, api_key: SecretStr, payload: dict) -> dict: + """Create a video using the Revid API.""" + url = "https://www.revid.ai/api/public/v2/render" + headers = {"key": api_key.get_secret_value()} + response = await Requests().post(url, json=payload, headers=headers) + logger.debug( + f"API Response Status Code: {response.status}, Content: {response.text}" + ) + return response.json() + + async def check_video_status(self, api_key: SecretStr, pid: str) -> dict: + """Check the status of a video creation job.""" + url = f"https://www.revid.ai/api/public/v2/status?pid={pid}" + headers = {"key": api_key.get_secret_value()} + response = await Requests().get(url, headers=headers) + return response.json() + + async def wait_for_video( + self, + api_key: SecretStr, + pid: str, + max_wait_time: int = 1000, + ) -> str: + """Wait for video creation to complete and return the video URL.""" + start_time = time.time() + while time.time() - start_time < max_wait_time: + status = await self.check_video_status(api_key, pid) + logger.debug(f"Video status: {status}") + + if status.get("status") == "ready" and "videoUrl" in status: + return status["videoUrl"] + elif status.get("status") == "error": + error_message = status.get("error", "Unknown error occurred") + logger.error(f"Video creation failed: {error_message}") + raise ValueError(f"Video creation failed: {error_message}") + elif status.get("status") in ["FAILED", "CANCELED"]: + logger.error(f"Video creation failed: {status.get('message')}") + raise ValueError(f"Video creation failed: {status.get('message')}") + + await asyncio.sleep(10) + + logger.error("Video creation timed out") + raise TimeoutError("Video creation timed out") + + def __init__(self): + super().__init__( + id="0f3e4635-e810-43d9-9e81-49e6f4e83b7c", + description="Turns a screenshot into an engaging, avatar‑narrated video advert.", + categories={BlockCategory.AI, BlockCategory.MARKETING}, + input_schema=AIScreenshotToVideoAdBlock.Input, + output_schema=AIScreenshotToVideoAdBlock.Output, + test_input={ + "credentials": TEST_CREDENTIALS_INPUT, + "script": "Amazing numbers!", + "screenshot_url": "https://cdn.revid.ai/uploads/1747080376028-image.png", + }, + test_output=("video_url", "https://example.com/screenshot.mp4"), + test_mock={ + "create_webhook": lambda *args, **kwargs: ( + "test_uuid", + "https://webhook.site/test_uuid", + ), + "create_video": lambda *args, **kwargs: {"pid": "test_pid"}, + "check_video_status": lambda *args, **kwargs: { + "status": "ready", + "videoUrl": "https://example.com/screenshot.mp4", + }, + "wait_for_video": lambda *args, **kwargs: "https://example.com/screenshot.mp4", + }, + test_credentials=TEST_CREDENTIALS, + ) + + async def run(self, input_data: Input, *, credentials: APIKeyCredentials, **kwargs): + webhook_token, webhook_url = await self.create_webhook() + + payload = { + "webhook": webhook_url, + "creationParams": { + "targetDuration": input_data.target_duration, + "ratio": input_data.ratio, + "mediaType": "aiVideo", + "hasAvatar": True, + "removeAvatarBackground": True, + "inputText": input_data.script, + "flowType": "text-to-video", + "slug": "ai-ad-generator", + "slugNew": "screenshot-to-video-ad", + "isCopiedFrom": "ai-ad-generator", + "hasToGenerateVoice": True, + "hasToTranscript": False, + "hasToSearchMedia": True, + "hasWebsiteRecorder": False, + "hasTextSmallAtBottom": False, + "selectedAudio": input_data.background_music.value, + "selectedVoice": input_data.voice.voice_id, + "selectedAvatar": "https://cdn.revid.ai/avatars/young-woman.mp4", + "selectedAvatarType": "video/mp4", + "websiteToRecord": "", + "hasToGenerateCover": True, + "nbGenerations": 1, + "disableCaptions": False, + "mediaMultiplier": "medium", + "characters": [], + "captionPresetName": "Revid", + "sourceType": "contentScraping", + "selectedStoryStyle": {"value": "custom", "label": "General"}, + "generationPreset": "DEFAULT", + "hasToGenerateMusic": False, + "isOptimizedForChinese": False, + "generationUserPrompt": "", + "enableNsfwFilter": False, + "addStickers": False, + "typeMovingImageAnim": "dynamic", + "hasToGenerateSoundEffects": False, + "forceModelType": "gpt-image-1", + "selectedCharacters": [], + "lang": "", + "voiceSpeed": 1, + "disableAudio": False, + "disableVoice": False, + "useOnlyProvidedMedia": True, + "imageGenerationModel": "ultra", + "videoGenerationModel": "ultra", + "hasEnhancedGeneration": True, + "hasEnhancedGenerationPro": True, + "inputMedias": [ + {"url": input_data.screenshot_url, "title": "", "type": "image"} + ], + "hasToGenerateVideos": True, + "audioUrl": input_data.background_music.audio_url, + "watermark": None, + }, + } + + response = await self.create_video(credentials.api_key, payload) + pid = response.get("pid") + if not pid: + raise RuntimeError("Failed to create video: No project ID returned") + + video_url = await self.wait_for_video(credentials.api_key, pid) + yield "video_url", video_url diff --git a/autogpt_platform/backend/backend/blocks/apollo/_api.py b/autogpt_platform/backend/backend/blocks/apollo/_api.py index dd4e6aa741..6a72ad313d 100644 --- a/autogpt_platform/backend/backend/blocks/apollo/_api.py +++ b/autogpt_platform/backend/backend/blocks/apollo/_api.py @@ -4,6 +4,7 @@ from typing import List from backend.blocks.apollo._auth import ApolloCredentials from backend.blocks.apollo.models import ( Contact, + EnrichPersonRequest, Organization, SearchOrganizationsRequest, SearchOrganizationsResponse, @@ -110,3 +111,21 @@ class ApolloClient: return ( organizations[: query.max_results] if query.max_results else organizations ) + + async def enrich_person(self, query: EnrichPersonRequest) -> Contact: + """Enrich a person's data including email & phone reveal""" + response = await self.requests.post( + f"{self.API_URL}/people/match", + headers=self._get_headers(), + json=query.model_dump(), + params={ + "reveal_personal_emails": "true", + }, + ) + data = response.json() + if "person" not in data: + raise ValueError(f"Person not found or enrichment failed: {data}") + + contact = Contact(**data["person"]) + contact.email = contact.email or "-" + return contact diff --git a/autogpt_platform/backend/backend/blocks/apollo/models.py b/autogpt_platform/backend/backend/blocks/apollo/models.py index f97da43eaf..4dde8f29b1 100644 --- a/autogpt_platform/backend/backend/blocks/apollo/models.py +++ b/autogpt_platform/backend/backend/blocks/apollo/models.py @@ -23,9 +23,9 @@ class BaseModel(OriginalBaseModel): class PrimaryPhone(BaseModel): """A primary phone in Apollo""" - number: str = "" - source: str = "" - sanitized_number: str = "" + number: Optional[str] = "" + source: Optional[str] = "" + sanitized_number: Optional[str] = "" class SenorityLevels(str, Enum): @@ -56,102 +56,102 @@ class ContactEmailStatuses(str, Enum): class RuleConfigStatus(BaseModel): """A rule config status in Apollo""" - _id: str = "" - created_at: str = "" - rule_action_config_id: str = "" - rule_config_id: str = "" - status_cd: str = "" - updated_at: str = "" - id: str = "" - key: str = "" + _id: Optional[str] = "" + created_at: Optional[str] = "" + rule_action_config_id: Optional[str] = "" + rule_config_id: Optional[str] = "" + status_cd: Optional[str] = "" + updated_at: Optional[str] = "" + id: Optional[str] = "" + key: Optional[str] = "" class ContactCampaignStatus(BaseModel): """A contact campaign status in Apollo""" - id: str = "" - emailer_campaign_id: str = "" - send_email_from_user_id: str = "" - inactive_reason: str = "" - status: str = "" - added_at: str = "" - added_by_user_id: str = "" - finished_at: str = "" - paused_at: str = "" - auto_unpause_at: str = "" - send_email_from_email_address: str = "" - send_email_from_email_account_id: str = "" - manually_set_unpause: str = "" - failure_reason: str = "" - current_step_id: str = "" - in_response_to_emailer_message_id: str = "" - cc_emails: str = "" - bcc_emails: str = "" - to_emails: str = "" + id: Optional[str] = "" + emailer_campaign_id: Optional[str] = "" + send_email_from_user_id: Optional[str] = "" + inactive_reason: Optional[str] = "" + status: Optional[str] = "" + added_at: Optional[str] = "" + added_by_user_id: Optional[str] = "" + finished_at: Optional[str] = "" + paused_at: Optional[str] = "" + auto_unpause_at: Optional[str] = "" + send_email_from_email_address: Optional[str] = "" + send_email_from_email_account_id: Optional[str] = "" + manually_set_unpause: Optional[str] = "" + failure_reason: Optional[str] = "" + current_step_id: Optional[str] = "" + in_response_to_emailer_message_id: Optional[str] = "" + cc_emails: Optional[str] = "" + bcc_emails: Optional[str] = "" + to_emails: Optional[str] = "" class Account(BaseModel): """An account in Apollo""" - id: str = "" - name: str = "" - website_url: str = "" - blog_url: str = "" - angellist_url: str = "" - linkedin_url: str = "" - twitter_url: str = "" - facebook_url: str = "" - primary_phone: PrimaryPhone = PrimaryPhone() - languages: list[str] - alexa_ranking: int = 0 - phone: str = "" - linkedin_uid: str = "" - founded_year: int = 0 - publicly_traded_symbol: str = "" - publicly_traded_exchange: str = "" - logo_url: str = "" - chrunchbase_url: str = "" - primary_domain: str = "" - domain: str = "" - team_id: str = "" - organization_id: str = "" - account_stage_id: str = "" - source: str = "" - original_source: str = "" - creator_id: str = "" - owner_id: str = "" - created_at: str = "" - phone_status: str = "" - hubspot_id: str = "" - salesforce_id: str = "" - crm_owner_id: str = "" - parent_account_id: str = "" - sanitized_phone: str = "" + id: Optional[str] = "" + name: Optional[str] = "" + website_url: Optional[str] = "" + blog_url: Optional[str] = "" + angellist_url: Optional[str] = "" + linkedin_url: Optional[str] = "" + twitter_url: Optional[str] = "" + facebook_url: Optional[str] = "" + primary_phone: Optional[PrimaryPhone] = PrimaryPhone() + languages: Optional[list[str]] = [] + alexa_ranking: Optional[int] = 0 + phone: Optional[str] = "" + linkedin_uid: Optional[str] = "" + founded_year: Optional[int] = 0 + publicly_traded_symbol: Optional[str] = "" + publicly_traded_exchange: Optional[str] = "" + logo_url: Optional[str] = "" + chrunchbase_url: Optional[str] = "" + primary_domain: Optional[str] = "" + domain: Optional[str] = "" + team_id: Optional[str] = "" + organization_id: Optional[str] = "" + account_stage_id: Optional[str] = "" + source: Optional[str] = "" + original_source: Optional[str] = "" + creator_id: Optional[str] = "" + owner_id: Optional[str] = "" + created_at: Optional[str] = "" + phone_status: Optional[str] = "" + hubspot_id: Optional[str] = "" + salesforce_id: Optional[str] = "" + crm_owner_id: Optional[str] = "" + parent_account_id: Optional[str] = "" + sanitized_phone: Optional[str] = "" # no listed type on the API docs - account_playbook_statues: list[Any] = [] - account_rule_config_statuses: list[RuleConfigStatus] = [] - existence_level: str = "" - label_ids: list[str] = [] - typed_custom_fields: Any - custom_field_errors: Any - modality: str = "" - source_display_name: str = "" - salesforce_record_id: str = "" - crm_record_url: str = "" + account_playbook_statues: Optional[list[Any]] = [] + account_rule_config_statuses: Optional[list[RuleConfigStatus]] = [] + existence_level: Optional[str] = "" + label_ids: Optional[list[str]] = [] + typed_custom_fields: Optional[Any] = {} + custom_field_errors: Optional[Any] = {} + modality: Optional[str] = "" + source_display_name: Optional[str] = "" + salesforce_record_id: Optional[str] = "" + crm_record_url: Optional[str] = "" class ContactEmail(BaseModel): """A contact email in Apollo""" - email: str = "" - email_md5: str = "" - email_sha256: str = "" - email_status: str = "" - email_source: str = "" - extrapolated_email_confidence: str = "" - position: int = 0 - email_from_customer: str = "" - free_domain: bool = True + email: Optional[str] = "" + email_md5: Optional[str] = "" + email_sha256: Optional[str] = "" + email_status: Optional[str] = "" + email_source: Optional[str] = "" + extrapolated_email_confidence: Optional[str] = "" + position: Optional[int] = 0 + email_from_customer: Optional[str] = "" + free_domain: Optional[bool] = True class EmploymentHistory(BaseModel): @@ -164,40 +164,40 @@ class EmploymentHistory(BaseModel): populate_by_name=True, ) - _id: Optional[str] = None - created_at: Optional[str] = None - current: Optional[bool] = None - degree: Optional[str] = None - description: Optional[str] = None - emails: Optional[str] = None - end_date: Optional[str] = None - grade_level: Optional[str] = None - kind: Optional[str] = None - major: Optional[str] = None - organization_id: Optional[str] = None - organization_name: Optional[str] = None - raw_address: Optional[str] = None - start_date: Optional[str] = None - title: Optional[str] = None - updated_at: Optional[str] = None - id: Optional[str] = None - key: Optional[str] = None + _id: Optional[str] = "" + created_at: Optional[str] = "" + current: Optional[bool] = False + degree: Optional[str] = "" + description: Optional[str] = "" + emails: Optional[str] = "" + end_date: Optional[str] = "" + grade_level: Optional[str] = "" + kind: Optional[str] = "" + major: Optional[str] = "" + organization_id: Optional[str] = "" + organization_name: Optional[str] = "" + raw_address: Optional[str] = "" + start_date: Optional[str] = "" + title: Optional[str] = "" + updated_at: Optional[str] = "" + id: Optional[str] = "" + key: Optional[str] = "" class Breadcrumb(BaseModel): """A breadcrumb in Apollo""" - label: Optional[str] = "N/A" - signal_field_name: Optional[str] = "N/A" - value: str | list | None = "N/A" - display_name: Optional[str] = "N/A" + label: Optional[str] = "" + signal_field_name: Optional[str] = "" + value: str | list | None = "" + display_name: Optional[str] = "" class TypedCustomField(BaseModel): """A typed custom field in Apollo""" - id: Optional[str] = "N/A" - value: Optional[str] = "N/A" + id: Optional[str] = "" + value: Optional[str] = "" class Pagination(BaseModel): @@ -219,23 +219,23 @@ class Pagination(BaseModel): class DialerFlags(BaseModel): """A dialer flags in Apollo""" - country_name: str = "" - country_enabled: bool - high_risk_calling_enabled: bool - potential_high_risk_number: bool + country_name: Optional[str] = "" + country_enabled: Optional[bool] = True + high_risk_calling_enabled: Optional[bool] = True + potential_high_risk_number: Optional[bool] = True class PhoneNumber(BaseModel): """A phone number in Apollo""" - raw_number: str = "" - sanitized_number: str = "" - type: str = "" - position: int = 0 - status: str = "" - dnc_status: str = "" - dnc_other_info: str = "" - dailer_flags: DialerFlags = DialerFlags( + raw_number: Optional[str] = "" + sanitized_number: Optional[str] = "" + type: Optional[str] = "" + position: Optional[int] = 0 + status: Optional[str] = "" + dnc_status: Optional[str] = "" + dnc_other_info: Optional[str] = "" + dailer_flags: Optional[DialerFlags] = DialerFlags( country_name="", country_enabled=True, high_risk_calling_enabled=True, @@ -253,33 +253,31 @@ class Organization(BaseModel): populate_by_name=True, ) - id: Optional[str] = "N/A" - name: Optional[str] = "N/A" - website_url: Optional[str] = "N/A" - blog_url: Optional[str] = "N/A" - angellist_url: Optional[str] = "N/A" - linkedin_url: Optional[str] = "N/A" - twitter_url: Optional[str] = "N/A" - facebook_url: Optional[str] = "N/A" - primary_phone: Optional[PrimaryPhone] = PrimaryPhone( - number="N/A", source="N/A", sanitized_number="N/A" - ) - languages: list[str] = [] + id: Optional[str] = "" + name: Optional[str] = "" + website_url: Optional[str] = "" + blog_url: Optional[str] = "" + angellist_url: Optional[str] = "" + linkedin_url: Optional[str] = "" + twitter_url: Optional[str] = "" + facebook_url: Optional[str] = "" + primary_phone: Optional[PrimaryPhone] = PrimaryPhone() + languages: Optional[list[str]] = [] alexa_ranking: Optional[int] = 0 - phone: Optional[str] = "N/A" - linkedin_uid: Optional[str] = "N/A" + phone: Optional[str] = "" + linkedin_uid: Optional[str] = "" founded_year: Optional[int] = 0 - publicly_traded_symbol: Optional[str] = "N/A" - publicly_traded_exchange: Optional[str] = "N/A" - logo_url: Optional[str] = "N/A" - chrunchbase_url: Optional[str] = "N/A" - primary_domain: Optional[str] = "N/A" - sanitized_phone: Optional[str] = "N/A" - owned_by_organization_id: Optional[str] = "N/A" - intent_strength: Optional[str] = "N/A" - show_intent: bool = True + publicly_traded_symbol: Optional[str] = "" + publicly_traded_exchange: Optional[str] = "" + logo_url: Optional[str] = "" + chrunchbase_url: Optional[str] = "" + primary_domain: Optional[str] = "" + sanitized_phone: Optional[str] = "" + owned_by_organization_id: Optional[str] = "" + intent_strength: Optional[str] = "" + show_intent: Optional[bool] = True has_intent_signal_account: Optional[bool] = True - intent_signal_account: Optional[str] = "N/A" + intent_signal_account: Optional[str] = "" class Contact(BaseModel): @@ -292,95 +290,95 @@ class Contact(BaseModel): populate_by_name=True, ) - contact_roles: list[Any] = [] - id: Optional[str] = None - first_name: Optional[str] = None - last_name: Optional[str] = None - name: Optional[str] = None - linkedin_url: Optional[str] = None - title: Optional[str] = None - contact_stage_id: Optional[str] = None - owner_id: Optional[str] = None - creator_id: Optional[str] = None - person_id: Optional[str] = None - email_needs_tickling: bool = True - organization_name: Optional[str] = None - source: Optional[str] = None - original_source: Optional[str] = None - organization_id: Optional[str] = None - headline: Optional[str] = None - photo_url: Optional[str] = None - present_raw_address: Optional[str] = None - linkededin_uid: Optional[str] = None - extrapolated_email_confidence: Optional[float] = None - salesforce_id: Optional[str] = None - salesforce_lead_id: Optional[str] = None - salesforce_contact_id: Optional[str] = None - saleforce_account_id: Optional[str] = None - crm_owner_id: Optional[str] = None - created_at: Optional[str] = None - emailer_campaign_ids: list[str] = [] - direct_dial_status: Optional[str] = None - direct_dial_enrichment_failed_at: Optional[str] = None - email_status: Optional[str] = None - email_source: Optional[str] = None - account_id: Optional[str] = None - last_activity_date: Optional[str] = None - hubspot_vid: Optional[str] = None - hubspot_company_id: Optional[str] = None - crm_id: Optional[str] = None - sanitized_phone: Optional[str] = None - merged_crm_ids: Optional[str] = None - updated_at: Optional[str] = None - queued_for_crm_push: bool = True - suggested_from_rule_engine_config_id: Optional[str] = None - email_unsubscribed: Optional[str] = None - label_ids: list[Any] = [] - has_pending_email_arcgate_request: bool = True - has_email_arcgate_request: bool = True - existence_level: Optional[str] = None - email: Optional[str] = None - email_from_customer: Optional[str] = None - typed_custom_fields: list[TypedCustomField] = [] - custom_field_errors: Any = None - salesforce_record_id: Optional[str] = None - crm_record_url: Optional[str] = None - email_status_unavailable_reason: Optional[str] = None - email_true_status: Optional[str] = None - updated_email_true_status: bool = True - contact_rule_config_statuses: list[RuleConfigStatus] = [] - source_display_name: Optional[str] = None - twitter_url: Optional[str] = None - contact_campaign_statuses: list[ContactCampaignStatus] = [] - state: Optional[str] = None - city: Optional[str] = None - country: Optional[str] = None - account: Optional[Account] = None - contact_emails: list[ContactEmail] = [] - organization: Optional[Organization] = None - employment_history: list[EmploymentHistory] = [] - time_zone: Optional[str] = None - intent_strength: Optional[str] = None - show_intent: bool = True - phone_numbers: list[PhoneNumber] = [] - account_phone_note: Optional[str] = None - free_domain: bool = True - is_likely_to_engage: bool = True - email_domain_catchall: bool = True - contact_job_change_event: Optional[str] = None + contact_roles: Optional[list[Any]] = [] + id: Optional[str] = "" + first_name: Optional[str] = "" + last_name: Optional[str] = "" + name: Optional[str] = "" + linkedin_url: Optional[str] = "" + title: Optional[str] = "" + contact_stage_id: Optional[str] = "" + owner_id: Optional[str] = "" + creator_id: Optional[str] = "" + person_id: Optional[str] = "" + email_needs_tickling: Optional[bool] = True + organization_name: Optional[str] = "" + source: Optional[str] = "" + original_source: Optional[str] = "" + organization_id: Optional[str] = "" + headline: Optional[str] = "" + photo_url: Optional[str] = "" + present_raw_address: Optional[str] = "" + linkededin_uid: Optional[str] = "" + extrapolated_email_confidence: Optional[float] = 0.0 + salesforce_id: Optional[str] = "" + salesforce_lead_id: Optional[str] = "" + salesforce_contact_id: Optional[str] = "" + saleforce_account_id: Optional[str] = "" + crm_owner_id: Optional[str] = "" + created_at: Optional[str] = "" + emailer_campaign_ids: Optional[list[str]] = [] + direct_dial_status: Optional[str] = "" + direct_dial_enrichment_failed_at: Optional[str] = "" + email_status: Optional[str] = "" + email_source: Optional[str] = "" + account_id: Optional[str] = "" + last_activity_date: Optional[str] = "" + hubspot_vid: Optional[str] = "" + hubspot_company_id: Optional[str] = "" + crm_id: Optional[str] = "" + sanitized_phone: Optional[str] = "" + merged_crm_ids: Optional[str] = "" + updated_at: Optional[str] = "" + queued_for_crm_push: Optional[bool] = True + suggested_from_rule_engine_config_id: Optional[str] = "" + email_unsubscribed: Optional[str] = "" + label_ids: Optional[list[Any]] = [] + has_pending_email_arcgate_request: Optional[bool] = True + has_email_arcgate_request: Optional[bool] = True + existence_level: Optional[str] = "" + email: Optional[str] = "" + email_from_customer: Optional[str] = "" + typed_custom_fields: Optional[list[TypedCustomField]] = [] + custom_field_errors: Optional[Any] = {} + salesforce_record_id: Optional[str] = "" + crm_record_url: Optional[str] = "" + email_status_unavailable_reason: Optional[str] = "" + email_true_status: Optional[str] = "" + updated_email_true_status: Optional[bool] = True + contact_rule_config_statuses: Optional[list[RuleConfigStatus]] = [] + source_display_name: Optional[str] = "" + twitter_url: Optional[str] = "" + contact_campaign_statuses: Optional[list[ContactCampaignStatus]] = [] + state: Optional[str] = "" + city: Optional[str] = "" + country: Optional[str] = "" + account: Optional[Account] = Account() + contact_emails: Optional[list[ContactEmail]] = [] + organization: Optional[Organization] = Organization() + employment_history: Optional[list[EmploymentHistory]] = [] + time_zone: Optional[str] = "" + intent_strength: Optional[str] = "" + show_intent: Optional[bool] = True + phone_numbers: Optional[list[PhoneNumber]] = [] + account_phone_note: Optional[str] = "" + free_domain: Optional[bool] = True + is_likely_to_engage: Optional[bool] = True + email_domain_catchall: Optional[bool] = True + contact_job_change_event: Optional[str] = "" class SearchOrganizationsRequest(BaseModel): """Request for Apollo's search organizations API""" - organization_num_empoloyees_range: list[int] = SchemaField( + organization_num_employees_range: Optional[list[int]] = SchemaField( description="""The number range of employees working for the company. This enables you to find companies based on headcount. You can add multiple ranges to expand your search results. Each range you add needs to be a string, with the upper and lower numbers of the range separated only by a comma.""", default=[0, 1000000], ) - organization_locations: list[str] = SchemaField( + organization_locations: Optional[list[str]] = SchemaField( description="""The location of the company headquarters. You can search across cities, US states, and countries. If a company has several office locations, results are still based on the headquarters location. For example, if you search chicago but a company's HQ location is in boston, any Boston-based companies will not appearch in your search results, even if they match other parameters. @@ -389,28 +387,30 @@ To exclude companies based on location, use the organization_not_locations param """, default_factory=list, ) - organizations_not_locations: list[str] = SchemaField( + organizations_not_locations: Optional[list[str]] = SchemaField( description="""Exclude companies from search results based on the location of the company headquarters. You can use cities, US states, and countries as locations to exclude. This parameter is useful for ensuring you do not prospect in an undesirable territory. For example, if you use ireland as a value, no Ireland-based companies will appear in your search results. """, default_factory=list, ) - q_organization_keyword_tags: list[str] = SchemaField( - description="""Filter search results based on keywords associated with companies. For example, you can enter mining as a value to return only companies that have an association with the mining industry.""" + q_organization_keyword_tags: Optional[list[str]] = SchemaField( + description="""Filter search results based on keywords associated with companies. For example, you can enter mining as a value to return only companies that have an association with the mining industry.""", + default_factory=list, ) - q_organization_name: str = SchemaField( + q_organization_name: Optional[str] = SchemaField( description="""Filter search results to include a specific company name. -If the value you enter for this parameter does not match with a company's name, the company will not appear in search results, even if it matches other parameters. Partial matches are accepted. For example, if you filter by the value marketing, a company called NY Marketing Unlimited would still be eligible as a search result, but NY Market Analysis would not be eligible.""" +If the value you enter for this parameter does not match with a company's name, the company will not appear in search results, even if it matches other parameters. Partial matches are accepted. For example, if you filter by the value marketing, a company called NY Marketing Unlimited would still be eligible as a search result, but NY Market Analysis would not be eligible.""", + default="", ) - organization_ids: list[str] = SchemaField( + organization_ids: Optional[list[str]] = SchemaField( description="""The Apollo IDs for the companies you want to include in your search results. Each company in the Apollo database is assigned a unique ID. To find IDs, identify the values for organization_id when you call this endpoint.""", default_factory=list, ) - max_results: int = SchemaField( + max_results: Optional[int] = SchemaField( description="""The maximum number of results to return. If you don't specify this parameter, the default is 100.""", default=100, ge=1, @@ -435,11 +435,11 @@ Use the page parameter to search the different pages of data.""", class SearchOrganizationsResponse(BaseModel): """Response from Apollo's search organizations API""" - breadcrumbs: list[Breadcrumb] = [] - partial_results_only: bool = True - has_join: bool = True - disable_eu_prospecting: bool = True - partial_results_limit: int = 0 + breadcrumbs: Optional[list[Breadcrumb]] = [] + partial_results_only: Optional[bool] = True + has_join: Optional[bool] = True + disable_eu_prospecting: Optional[bool] = True + partial_results_limit: Optional[int] = 0 pagination: Pagination = Pagination( page=0, per_page=0, total_entries=0, total_pages=0 ) @@ -447,14 +447,14 @@ class SearchOrganizationsResponse(BaseModel): accounts: list[Any] = [] organizations: list[Organization] = [] models_ids: list[str] = [] - num_fetch_result: Optional[str] = "N/A" - derived_params: Optional[str] = "N/A" + num_fetch_result: Optional[str] = "" + derived_params: Optional[str] = "" class SearchPeopleRequest(BaseModel): """Request for Apollo's search people API""" - person_titles: list[str] = SchemaField( + person_titles: Optional[list[str]] = SchemaField( description="""Job titles held by the people you want to find. For a person to be included in search results, they only need to match 1 of the job titles you add. Adding more job titles expands your search results. Results also include job titles with the same terms, even if they are not exact matches. For example, searching for marketing manager might return people with the job title content marketing manager. @@ -464,13 +464,13 @@ Use this parameter in combination with the person_seniorities[] parameter to fin default_factory=list, placeholder="marketing manager", ) - person_locations: list[str] = SchemaField( + person_locations: Optional[list[str]] = SchemaField( description="""The location where people live. You can search across cities, US states, and countries. To find people based on the headquarters locations of their current employer, use the organization_locations parameter.""", default_factory=list, ) - person_seniorities: list[SenorityLevels] = SchemaField( + person_seniorities: Optional[list[SenorityLevels]] = SchemaField( description="""The job seniority that people hold within their current employer. This enables you to find people that currently hold positions at certain reporting levels, such as Director level or senior IC level. For a person to be included in search results, they only need to match 1 of the seniorities you add. Adding more seniorities expands your search results. @@ -480,7 +480,7 @@ Searches only return results based on their current job title, so searching for Use this parameter in combination with the person_titles[] parameter to find people based on specific job functions and seniority levels.""", default_factory=list, ) - organization_locations: list[str] = SchemaField( + organization_locations: Optional[list[str]] = SchemaField( description="""The location of the company headquarters for a person's current employer. You can search across cities, US states, and countries. If a company has several office locations, results are still based on the headquarters location. For example, if you search chicago but a company's HQ location is in boston, people that work for the Boston-based company will not appear in your results, even if they match other parameters. @@ -488,7 +488,7 @@ If a company has several office locations, results are still based on the headqu To find people based on their personal location, use the person_locations parameter.""", default_factory=list, ) - q_organization_domains: list[str] = SchemaField( + q_organization_domains: Optional[list[str]] = SchemaField( description="""The domain name for the person's employer. This can be the current employer or a previous employer. Do not include www., the @ symbol, or similar. You can add multiple domains to search across companies. @@ -496,23 +496,23 @@ You can add multiple domains to search across companies. Examples: apollo.io and microsoft.com""", default_factory=list, ) - contact_email_statuses: list[ContactEmailStatuses] = SchemaField( + contact_email_statuses: Optional[list[ContactEmailStatuses]] = SchemaField( description="""The email statuses for the people you want to find. You can add multiple statuses to expand your search.""", default_factory=list, ) - organization_ids: list[str] = SchemaField( + organization_ids: Optional[list[str]] = SchemaField( description="""The Apollo IDs for the companies (employers) you want to include in your search results. Each company in the Apollo database is assigned a unique ID. To find IDs, call the Organization Search endpoint and identify the values for organization_id.""", default_factory=list, ) - organization_num_empoloyees_range: list[int] = SchemaField( + organization_num_employees_range: Optional[list[int]] = SchemaField( description="""The number range of employees working for the company. This enables you to find companies based on headcount. You can add multiple ranges to expand your search results. Each range you add needs to be a string, with the upper and lower numbers of the range separated only by a comma.""", default_factory=list, ) - q_keywords: str = SchemaField( + q_keywords: Optional[str] = SchemaField( description="""A string of words over which we want to filter the results""", default="", ) @@ -528,7 +528,7 @@ Use this parameter in combination with the per_page parameter to make search res Use the page parameter to search the different pages of data.""", default=100, ) - max_results: int = SchemaField( + max_results: Optional[int] = SchemaField( description="""The maximum number of results to return. If you don't specify this parameter, the default is 100.""", default=100, ge=1, @@ -547,16 +547,61 @@ class SearchPeopleResponse(BaseModel): populate_by_name=True, ) - breadcrumbs: list[Breadcrumb] = [] - partial_results_only: bool = True - has_join: bool = True - disable_eu_prospecting: bool = True - partial_results_limit: int = 0 + breadcrumbs: Optional[list[Breadcrumb]] = [] + partial_results_only: Optional[bool] = True + has_join: Optional[bool] = True + disable_eu_prospecting: Optional[bool] = True + partial_results_limit: Optional[int] = 0 pagination: Pagination = Pagination( page=0, per_page=0, total_entries=0, total_pages=0 ) contacts: list[Contact] = [] people: list[Contact] = [] model_ids: list[str] = [] - num_fetch_result: Optional[str] = "N/A" - derived_params: Optional[str] = "N/A" + num_fetch_result: Optional[str] = "" + derived_params: Optional[str] = "" + + +class EnrichPersonRequest(BaseModel): + """Request for Apollo's person enrichment API""" + + person_id: Optional[str] = SchemaField( + description="Apollo person ID to enrich (most accurate method)", + default="", + ) + first_name: Optional[str] = SchemaField( + description="First name of the person to enrich", + default="", + ) + last_name: Optional[str] = SchemaField( + description="Last name of the person to enrich", + default="", + ) + name: Optional[str] = SchemaField( + description="Full name of the person to enrich", + default="", + ) + email: Optional[str] = SchemaField( + description="Email address of the person to enrich", + default="", + ) + domain: Optional[str] = SchemaField( + description="Company domain of the person to enrich", + default="", + ) + company: Optional[str] = SchemaField( + description="Company name of the person to enrich", + default="", + ) + linkedin_url: Optional[str] = SchemaField( + description="LinkedIn URL of the person to enrich", + default="", + ) + organization_id: Optional[str] = SchemaField( + description="Apollo organization ID of the person's company", + default="", + ) + title: Optional[str] = SchemaField( + description="Job title of the person to enrich", + default="", + ) diff --git a/autogpt_platform/backend/backend/blocks/apollo/organization.py b/autogpt_platform/backend/backend/blocks/apollo/organization.py index e21b0ab5d9..10abec0825 100644 --- a/autogpt_platform/backend/backend/blocks/apollo/organization.py +++ b/autogpt_platform/backend/backend/blocks/apollo/organization.py @@ -11,14 +11,14 @@ from backend.blocks.apollo.models import ( SearchOrganizationsRequest, ) from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema -from backend.data.model import SchemaField +from backend.data.model import CredentialsField, SchemaField class SearchOrganizationsBlock(Block): """Search for organizations in Apollo""" class Input(BlockSchema): - organization_num_empoloyees_range: list[int] = SchemaField( + organization_num_employees_range: list[int] = SchemaField( description="""The number range of employees working for the company. This enables you to find companies based on headcount. You can add multiple ranges to expand your search results. Each range you add needs to be a string, with the upper and lower numbers of the range separated only by a comma.""", @@ -65,7 +65,7 @@ To find IDs, identify the values for organization_id when you call this endpoint le=50000, advanced=True, ) - credentials: ApolloCredentialsInput = SchemaField( + credentials: ApolloCredentialsInput = CredentialsField( description="Apollo credentials", ) diff --git a/autogpt_platform/backend/backend/blocks/apollo/people.py b/autogpt_platform/backend/backend/blocks/apollo/people.py index c6d8620b7d..0ef35cd445 100644 --- a/autogpt_platform/backend/backend/blocks/apollo/people.py +++ b/autogpt_platform/backend/backend/blocks/apollo/people.py @@ -1,3 +1,5 @@ +import asyncio + from backend.blocks.apollo._api import ApolloClient from backend.blocks.apollo._auth import ( TEST_CREDENTIALS, @@ -8,11 +10,12 @@ from backend.blocks.apollo._auth import ( from backend.blocks.apollo.models import ( Contact, ContactEmailStatuses, + EnrichPersonRequest, SearchPeopleRequest, SenorityLevels, ) from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema -from backend.data.model import SchemaField +from backend.data.model import CredentialsField, SchemaField class SearchPeopleBlock(Block): @@ -77,7 +80,7 @@ class SearchPeopleBlock(Block): default_factory=list, advanced=False, ) - organization_num_empoloyees_range: list[int] = SchemaField( + organization_num_employees_range: list[int] = SchemaField( description="""The number range of employees working for the company. This enables you to find companies based on headcount. You can add multiple ranges to expand your search results. Each range you add needs to be a string, with the upper and lower numbers of the range separated only by a comma.""", @@ -90,14 +93,19 @@ class SearchPeopleBlock(Block): advanced=False, ) max_results: int = SchemaField( - description="""The maximum number of results to return. If you don't specify this parameter, the default is 100.""", - default=100, + description="""The maximum number of results to return. If you don't specify this parameter, the default is 25. Limited to 500 to prevent overspending.""", + default=25, ge=1, - le=50000, + le=500, + advanced=True, + ) + enrich_info: bool = SchemaField( + description="""Whether to enrich contacts with detailed information including real email addresses. This will double the search cost.""", + default=False, advanced=True, ) - credentials: ApolloCredentialsInput = SchemaField( + credentials: ApolloCredentialsInput = CredentialsField( description="Apollo credentials", ) @@ -106,10 +114,6 @@ class SearchPeopleBlock(Block): description="List of people found", default_factory=list, ) - person: Contact = SchemaField( - title="Person", - description="Each found person, one at a time", - ) error: str = SchemaField( description="Error message if the search failed", default="", @@ -125,87 +129,6 @@ class SearchPeopleBlock(Block): test_credentials=TEST_CREDENTIALS, test_input={"credentials": TEST_CREDENTIALS_INPUT}, test_output=[ - ( - "person", - Contact( - contact_roles=[], - id="1", - name="John Doe", - first_name="John", - last_name="Doe", - linkedin_url="https://www.linkedin.com/in/johndoe", - title="Software Engineer", - organization_name="Google", - organization_id="123456", - contact_stage_id="1", - owner_id="1", - creator_id="1", - person_id="1", - email_needs_tickling=True, - source="apollo", - original_source="apollo", - headline="Software Engineer", - photo_url="https://www.linkedin.com/in/johndoe", - present_raw_address="123 Main St, Anytown, USA", - linkededin_uid="123456", - extrapolated_email_confidence=0.8, - salesforce_id="123456", - salesforce_lead_id="123456", - salesforce_contact_id="123456", - saleforce_account_id="123456", - crm_owner_id="123456", - created_at="2021-01-01", - emailer_campaign_ids=[], - direct_dial_status="active", - direct_dial_enrichment_failed_at="2021-01-01", - email_status="active", - email_source="apollo", - account_id="123456", - last_activity_date="2021-01-01", - hubspot_vid="123456", - hubspot_company_id="123456", - crm_id="123456", - sanitized_phone="123456", - merged_crm_ids="123456", - updated_at="2021-01-01", - queued_for_crm_push=True, - suggested_from_rule_engine_config_id="123456", - email_unsubscribed=None, - label_ids=[], - has_pending_email_arcgate_request=True, - has_email_arcgate_request=True, - existence_level=None, - email=None, - email_from_customer=None, - typed_custom_fields=[], - custom_field_errors=None, - salesforce_record_id=None, - crm_record_url=None, - email_status_unavailable_reason=None, - email_true_status=None, - updated_email_true_status=True, - contact_rule_config_statuses=[], - source_display_name=None, - twitter_url=None, - contact_campaign_statuses=[], - state=None, - city=None, - country=None, - account=None, - contact_emails=[], - organization=None, - employment_history=[], - time_zone=None, - intent_strength=None, - show_intent=True, - phone_numbers=[], - account_phone_note=None, - free_domain=True, - is_likely_to_engage=True, - email_domain_catchall=True, - contact_job_change_event=None, - ), - ), ( "people", [ @@ -380,6 +303,34 @@ class SearchPeopleBlock(Block): client = ApolloClient(credentials) return await client.search_people(query) + @staticmethod + async def enrich_person( + query: EnrichPersonRequest, credentials: ApolloCredentials + ) -> Contact: + client = ApolloClient(credentials) + return await client.enrich_person(query) + + @staticmethod + def merge_contact_data(original: Contact, enriched: Contact) -> Contact: + """ + Merge contact data from original search with enriched data. + Enriched data complements original data, only filling in missing values. + """ + merged_data = original.model_dump() + enriched_data = enriched.model_dump() + + # Only update fields that are None, empty string, empty list, or default values in original + for key, enriched_value in enriched_data.items(): + # Skip if enriched value is None, empty string, or empty list + if enriched_value is None or enriched_value == "" or enriched_value == []: + continue + + # Update if original value is None, empty string, empty list, or zero + if enriched_value: + merged_data[key] = enriched_value + + return Contact(**merged_data) + async def run( self, input_data: Input, @@ -390,6 +341,23 @@ class SearchPeopleBlock(Block): query = SearchPeopleRequest(**input_data.model_dump()) people = await self.search_people(query, credentials) - for person in people: - yield "person", person + + # Enrich with detailed info if requested + if input_data.enrich_info: + + async def enrich_or_fallback(person: Contact): + try: + enrich_query = EnrichPersonRequest(person_id=person.id) + enriched_person = await self.enrich_person( + enrich_query, credentials + ) + # Merge enriched data with original data, complementing instead of replacing + return self.merge_contact_data(person, enriched_person) + except Exception: + return person # If enrichment fails, use original person data + + people = await asyncio.gather( + *(enrich_or_fallback(person) for person in people) + ) + yield "people", people diff --git a/autogpt_platform/backend/backend/blocks/apollo/person.py b/autogpt_platform/backend/backend/blocks/apollo/person.py new file mode 100644 index 0000000000..dad8ab733f --- /dev/null +++ b/autogpt_platform/backend/backend/blocks/apollo/person.py @@ -0,0 +1,138 @@ +from backend.blocks.apollo._api import ApolloClient +from backend.blocks.apollo._auth import ( + TEST_CREDENTIALS, + TEST_CREDENTIALS_INPUT, + ApolloCredentials, + ApolloCredentialsInput, +) +from backend.blocks.apollo.models import Contact, EnrichPersonRequest +from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema +from backend.data.model import CredentialsField, SchemaField + + +class GetPersonDetailBlock(Block): + """Get detailed person data with Apollo API, including email reveal""" + + class Input(BlockSchema): + person_id: str = SchemaField( + description="Apollo person ID to enrich (most accurate method)", + default="", + advanced=False, + ) + first_name: str = SchemaField( + description="First name of the person to enrich", + default="", + advanced=False, + ) + last_name: str = SchemaField( + description="Last name of the person to enrich", + default="", + advanced=False, + ) + name: str = SchemaField( + description="Full name of the person to enrich (alternative to first_name + last_name)", + default="", + advanced=False, + ) + email: str = SchemaField( + description="Known email address of the person (helps with matching)", + default="", + advanced=False, + ) + domain: str = SchemaField( + description="Company domain of the person (e.g., 'google.com')", + default="", + advanced=False, + ) + company: str = SchemaField( + description="Company name of the person", + default="", + advanced=False, + ) + linkedin_url: str = SchemaField( + description="LinkedIn URL of the person", + default="", + advanced=False, + ) + organization_id: str = SchemaField( + description="Apollo organization ID of the person's company", + default="", + advanced=True, + ) + title: str = SchemaField( + description="Job title of the person to enrich", + default="", + advanced=True, + ) + credentials: ApolloCredentialsInput = CredentialsField( + description="Apollo credentials", + ) + + class Output(BlockSchema): + contact: Contact = SchemaField( + description="Enriched contact information", + ) + error: str = SchemaField( + description="Error message if enrichment failed", + default="", + ) + + def __init__(self): + super().__init__( + id="3b18d46c-3db6-42ae-a228-0ba441bdd176", + description="Get detailed person data with Apollo API, including email reveal", + categories={BlockCategory.SEARCH}, + input_schema=GetPersonDetailBlock.Input, + output_schema=GetPersonDetailBlock.Output, + test_credentials=TEST_CREDENTIALS, + test_input={ + "credentials": TEST_CREDENTIALS_INPUT, + "first_name": "John", + "last_name": "Doe", + "company": "Google", + }, + test_output=[ + ( + "contact", + Contact( + id="1", + name="John Doe", + first_name="John", + last_name="Doe", + email="john.doe@gmail.com", + title="Software Engineer", + organization_name="Google", + linkedin_url="https://www.linkedin.com/in/johndoe", + ), + ), + ], + test_mock={ + "enrich_person": lambda query, credentials: Contact( + id="1", + name="John Doe", + first_name="John", + last_name="Doe", + email="john.doe@gmail.com", + title="Software Engineer", + organization_name="Google", + linkedin_url="https://www.linkedin.com/in/johndoe", + ) + }, + ) + + @staticmethod + async def enrich_person( + query: EnrichPersonRequest, credentials: ApolloCredentials + ) -> Contact: + client = ApolloClient(credentials) + return await client.enrich_person(query) + + async def run( + self, + input_data: Input, + *, + credentials: ApolloCredentials, + **kwargs, + ) -> BlockOutput: + query = EnrichPersonRequest(**input_data.model_dump()) + yield "contact", await self.enrich_person(query, credentials) diff --git a/autogpt_platform/backend/backend/blocks/basic.py b/autogpt_platform/backend/backend/blocks/basic.py index 7e52e70f12..13d3b4e62f 100644 --- a/autogpt_platform/backend/backend/blocks/basic.py +++ b/autogpt_platform/backend/backend/blocks/basic.py @@ -6,6 +6,7 @@ from backend.data.model import SchemaField from backend.util import json from backend.util.file import store_media_file from backend.util.mock import MockObject +from backend.util.prompt import estimate_token_count_str from backend.util.type import MediaFileType, convert @@ -14,6 +15,12 @@ class FileStoreBlock(Block): file_in: MediaFileType = SchemaField( description="The file to store in the temporary directory, it can be a URL, data URI, or local path." ) + base_64: bool = SchemaField( + description="Whether produce an output in base64 format (not recommended, you can pass the string path just fine accross blocks).", + default=False, + advanced=True, + title="Produce Base64 Output", + ) class Output(BlockSchema): file_out: MediaFileType = SchemaField( @@ -37,12 +44,11 @@ class FileStoreBlock(Block): graph_exec_id: str, **kwargs, ) -> BlockOutput: - file_path = await store_media_file( + yield "file_out", await store_media_file( graph_exec_id=graph_exec_id, file=input_data.file_in, - return_content=False, + return_content=input_data.base_64, ) - yield "file_out", file_path class StoreValueBlock(Block): @@ -461,6 +467,11 @@ class CreateListBlock(Block): description="Maximum size of the list. If provided, the list will be yielded in chunks of this size.", advanced=True, ) + max_tokens: int | None = SchemaField( + default=None, + description="Maximum tokens for the list. If provided, the list will be yielded in chunks that fit within this token limit.", + advanced=True, + ) class Output(BlockSchema): list: List[Any] = SchemaField( @@ -471,7 +482,7 @@ class CreateListBlock(Block): def __init__(self): super().__init__( id="a912d5c7-6e00-4542-b2a9-8034136930e4", - description="Creates a list with the specified values. Use this when you know all the values you want to add upfront.", + description="Creates a list with the specified values. Use this when you know all the values you want to add upfront. This block can also yield the list in batches based on a maximum size or token limit.", categories={BlockCategory.DATA}, input_schema=CreateListBlock.Input, output_schema=CreateListBlock.Output, @@ -496,12 +507,30 @@ class CreateListBlock(Block): ) async def run(self, input_data: Input, **kwargs) -> BlockOutput: - try: - max_size = input_data.max_size or len(input_data.values) - for i in range(0, len(input_data.values), max_size): - yield "list", input_data.values[i : i + max_size] - except Exception as e: - yield "error", f"Failed to create list: {str(e)}" + chunk = [] + cur_tokens, max_tokens = 0, input_data.max_tokens + cur_size, max_size = 0, input_data.max_size + + for value in input_data.values: + if max_tokens: + tokens = estimate_token_count_str(value) + else: + tokens = 0 + + # Check if adding this value would exceed either limit + if (max_tokens and (cur_tokens + tokens > max_tokens)) or ( + max_size and (cur_size + 1 > max_size) + ): + yield "list", chunk + chunk = [value] + cur_size, cur_tokens = 1, tokens + else: + chunk.append(value) + cur_size, cur_tokens = cur_size + 1, cur_tokens + tokens + + # Yield final chunk if any + if chunk: + yield "list", chunk class TypeOptions(enum.Enum): diff --git a/autogpt_platform/backend/backend/blocks/branching.py b/autogpt_platform/backend/backend/blocks/branching.py index 17cfd6d5c1..fa66c2d30d 100644 --- a/autogpt_platform/backend/backend/blocks/branching.py +++ b/autogpt_platform/backend/backend/blocks/branching.py @@ -3,6 +3,7 @@ from typing import Any from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema from backend.data.model import SchemaField +from backend.util.type import convert class ComparisonOperator(Enum): @@ -181,7 +182,23 @@ class IfInputMatchesBlock(Block): ) async def run(self, input_data: Input, **kwargs) -> BlockOutput: - if input_data.input == input_data.value or input_data.input is input_data.value: + + # If input_data.value is not matching input_data.input, convert value to type of input + if ( + input_data.input != input_data.value + and input_data.input is not input_data.value + ): + try: + # Only attempt conversion if input is not None and value is not None + if input_data.input is not None and input_data.value is not None: + input_type = type(input_data.input) + # Avoid converting if input_type is Any or object + if input_type not in (Any, object): + input_data.value = convert(input_data.value, input_type) + except Exception: + pass # If conversion fails, just leave value as is + + if input_data.input == input_data.value: yield "result", True yield "yes_output", input_data.yes_value else: diff --git a/autogpt_platform/backend/backend/blocks/flux_kontext.py b/autogpt_platform/backend/backend/blocks/flux_kontext.py index f391b41939..cab8358b2d 100644 --- a/autogpt_platform/backend/backend/blocks/flux_kontext.py +++ b/autogpt_platform/backend/backend/blocks/flux_kontext.py @@ -13,7 +13,7 @@ from backend.data.model import ( SchemaField, ) from backend.integrations.providers import ProviderName -from backend.util.file import MediaFileType +from backend.util.file import MediaFileType, store_media_file TEST_CREDENTIALS = APIKeyCredentials( id="01234567-89ab-cdef-0123-456789abcdef", @@ -108,7 +108,7 @@ class AIImageEditorBlock(Block): output_schema=AIImageEditorBlock.Output, test_input={ "prompt": "Add a hat to the cat", - "input_image": "https://example.com/cat.png", + "input_image": "data:image/png;base64,MQ==", "aspect_ratio": AspectRatio.MATCH_INPUT_IMAGE, "seed": None, "model": FluxKontextModelName.PRO, @@ -128,13 +128,22 @@ class AIImageEditorBlock(Block): input_data: Input, *, credentials: APIKeyCredentials, + graph_exec_id: str, **kwargs, ) -> BlockOutput: result = await self.run_model( api_key=credentials.api_key, model_name=input_data.model.api_name, prompt=input_data.prompt, - input_image=input_data.input_image, + input_image_b64=( + await store_media_file( + graph_exec_id=graph_exec_id, + file=input_data.input_image, + return_content=True, + ) + if input_data.input_image + else None + ), aspect_ratio=input_data.aspect_ratio.value, seed=input_data.seed, ) @@ -145,14 +154,14 @@ class AIImageEditorBlock(Block): api_key: SecretStr, model_name: str, prompt: str, - input_image: Optional[MediaFileType], + input_image_b64: Optional[str], aspect_ratio: str, seed: Optional[int], ) -> MediaFileType: client = ReplicateClient(api_token=api_key.get_secret_value()) input_params = { "prompt": prompt, - "input_image": input_image, + "input_image": input_image_b64, "aspect_ratio": aspect_ratio, **({"seed": seed} if seed is not None else {}), } diff --git a/autogpt_platform/backend/backend/blocks/github/pull_requests.py b/autogpt_platform/backend/backend/blocks/github/pull_requests.py index dbb940217c..581730fe3d 100644 --- a/autogpt_platform/backend/backend/blocks/github/pull_requests.py +++ b/autogpt_platform/backend/backend/blocks/github/pull_requests.py @@ -265,10 +265,26 @@ class GithubReadPullRequestBlock(Block): files = response.json() changes = [] for file in files: - filename = file.get("filename", "") - status = file.get("status", "") - changes.append(f"{filename}: {status}") - return "\n".join(changes) + status: str = file.get("status", "") + diff: str = file.get("patch", "") + if status != "removed": + is_filename: str = file.get("filename", "") + was_filename: str = ( + file.get("previous_filename", is_filename) + if status != "added" + else "" + ) + else: + is_filename = "" + was_filename: str = file.get("filename", "") + + patch_header = "" + if was_filename: + patch_header += f"--- {was_filename}\n" + if is_filename: + patch_header += f"+++ {is_filename}\n" + changes.append(patch_header + diff) + return "\n\n".join(changes) async def run( self, diff --git a/autogpt_platform/backend/backend/blocks/http.py b/autogpt_platform/backend/backend/blocks/http.py index 9bb6d9b55e..9d6d77fa3f 100644 --- a/autogpt_platform/backend/backend/blocks/http.py +++ b/autogpt_platform/backend/backend/blocks/http.py @@ -3,11 +3,19 @@ import logging from enum import Enum from io import BytesIO from pathlib import Path +from typing import Literal import aiofiles +from pydantic import SecretStr from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema -from backend.data.model import SchemaField +from backend.data.model import ( + CredentialsField, + CredentialsMetaInput, + HostScopedCredentials, + SchemaField, +) +from backend.integrations.providers import ProviderName from backend.util.file import ( MediaFileType, get_exec_file_path, @@ -19,6 +27,30 @@ from backend.util.request import Requests logger = logging.getLogger(name=__name__) +# Host-scoped credentials for HTTP requests +HttpCredentials = CredentialsMetaInput[ + Literal[ProviderName.HTTP], Literal["host_scoped"] +] + + +TEST_CREDENTIALS = HostScopedCredentials( + id="01234567-89ab-cdef-0123-456789abcdef", + provider="http", + host="api.example.com", + headers={ + "Authorization": SecretStr("Bearer test-token"), + "X-API-Key": SecretStr("test-api-key"), + }, + title="Mock HTTP Host-Scoped Credentials", +) +TEST_CREDENTIALS_INPUT = { + "provider": TEST_CREDENTIALS.provider, + "id": TEST_CREDENTIALS.id, + "type": TEST_CREDENTIALS.type, + "title": TEST_CREDENTIALS.title, +} + + class HttpMethod(Enum): GET = "GET" POST = "POST" @@ -169,3 +201,62 @@ class SendWebRequestBlock(Block): yield "client_error", result else: yield "server_error", result + + +class SendAuthenticatedWebRequestBlock(SendWebRequestBlock): + class Input(SendWebRequestBlock.Input): + credentials: HttpCredentials = CredentialsField( + description="HTTP host-scoped credentials for automatic header injection", + discriminator="url", + ) + + def __init__(self): + Block.__init__( + self, + id="fff86bcd-e001-4bad-a7f6-2eae4720c8dc", + description="Make an authenticated HTTP request with host-scoped credentials (JSON / form / multipart).", + categories={BlockCategory.OUTPUT}, + input_schema=SendAuthenticatedWebRequestBlock.Input, + output_schema=SendWebRequestBlock.Output, + test_credentials=TEST_CREDENTIALS, + ) + + async def run( # type: ignore[override] + self, + input_data: Input, + *, + graph_exec_id: str, + credentials: HostScopedCredentials, + **kwargs, + ) -> BlockOutput: + # Create SendWebRequestBlock.Input from our input (removing credentials field) + base_input = SendWebRequestBlock.Input( + url=input_data.url, + method=input_data.method, + headers=input_data.headers, + json_format=input_data.json_format, + body=input_data.body, + files_name=input_data.files_name, + files=input_data.files, + ) + + # Apply host-scoped credentials to headers + extra_headers = {} + if credentials.matches_url(input_data.url): + logger.debug( + f"Applying host-scoped credentials {credentials.id} for URL {input_data.url}" + ) + extra_headers.update(credentials.get_headers_dict()) + else: + logger.warning( + f"Host-scoped credentials {credentials.id} do not match URL {input_data.url}" + ) + + # Merge with user-provided headers (user headers take precedence) + base_input.headers = {**extra_headers, **input_data.headers} + + # Use parent class run method + async for output_name, output_data in super().run( + base_input, graph_exec_id=graph_exec_id, **kwargs + ): + yield output_name, output_data diff --git a/autogpt_platform/backend/backend/blocks/io.py b/autogpt_platform/backend/backend/blocks/io.py index c42e6c1dd7..56abaaf38e 100644 --- a/autogpt_platform/backend/backend/blocks/io.py +++ b/autogpt_platform/backend/backend/blocks/io.py @@ -413,6 +413,12 @@ class AgentFileInputBlock(AgentInputBlock): advanced=False, title="Default Value", ) + base_64: bool = SchemaField( + description="Whether produce an output in base64 format (not recommended, you can pass the string path just fine accross blocks).", + default=False, + advanced=True, + title="Produce Base64 Output", + ) class Output(AgentInputBlock.Output): result: str = SchemaField(description="File reference/path result.") @@ -446,12 +452,11 @@ class AgentFileInputBlock(AgentInputBlock): if not input_data.value: return - file_path = await store_media_file( + yield "result", await store_media_file( graph_exec_id=graph_exec_id, file=input_data.value, - return_content=False, + return_content=input_data.base_64, ) - yield "result", file_path class AgentDropdownInputBlock(AgentInputBlock): diff --git a/autogpt_platform/backend/backend/blocks/llm.py b/autogpt_platform/backend/backend/blocks/llm.py index 5230cb4428..6e83190ffd 100644 --- a/autogpt_platform/backend/backend/blocks/llm.py +++ b/autogpt_platform/backend/backend/blocks/llm.py @@ -23,6 +23,7 @@ from backend.data.model import ( from backend.integrations.providers import ProviderName from backend.util import json from backend.util.logging import TruncatedLogger +from backend.util.prompt import compress_prompt, estimate_token_count from backend.util.text import TextFormatter logger = TruncatedLogger(logging.getLogger(__name__), "[LLM-Block]") @@ -40,7 +41,7 @@ LLMProviderName = Literal[ AICredentials = CredentialsMetaInput[LLMProviderName, Literal["api_key"]] TEST_CREDENTIALS = APIKeyCredentials( - id="ed55ac19-356e-4243-a6cb-bc599e9b716f", + id="769f6af7-820b-4d5d-9b7a-ab82bbc165f", provider="openai", api_key=SecretStr("mock-openai-api-key"), title="Mock OpenAI API key", @@ -306,13 +307,6 @@ def convert_openai_tool_fmt_to_anthropic( return anthropic_tools -def estimate_token_count(prompt_messages: list[dict]) -> int: - char_count = sum(len(str(msg.get("content", ""))) for msg in prompt_messages) - message_overhead = len(prompt_messages) * 4 - estimated_tokens = (char_count // 4) + message_overhead - return int(estimated_tokens * 1.2) - - async def llm_call( credentials: APIKeyCredentials, llm_model: LlmModel, @@ -321,7 +315,8 @@ async def llm_call( max_tokens: int | None, tools: list[dict] | None = None, ollama_host: str = "localhost:11434", - parallel_tool_calls: bool | None = None, + parallel_tool_calls=None, + compress_prompt_to_fit: bool = True, ) -> LLMResponse: """ Make a call to a language model. @@ -344,10 +339,17 @@ async def llm_call( - completion_tokens: The number of tokens used in the completion. """ provider = llm_model.metadata.provider + context_window = llm_model.context_window + + if compress_prompt_to_fit: + prompt = compress_prompt( + messages=prompt, + target_tokens=llm_model.context_window // 2, + lossy_ok=True, + ) # Calculate available tokens based on context window and input length estimated_input_tokens = estimate_token_count(prompt) - context_window = llm_model.context_window model_max_output = llm_model.max_output_tokens or int(2**15) user_max = max_tokens or model_max_output available_tokens = max(context_window - estimated_input_tokens, 0) @@ -358,14 +360,10 @@ async def llm_call( oai_client = openai.AsyncOpenAI(api_key=credentials.api_key.get_secret_value()) response_format = None - if llm_model in [LlmModel.O1_MINI, LlmModel.O1_PREVIEW]: - sys_messages = [p["content"] for p in prompt if p["role"] == "system"] - usr_messages = [p["content"] for p in prompt if p["role"] != "system"] - prompt = [ - {"role": "user", "content": "\n".join(sys_messages)}, - {"role": "user", "content": "\n".join(usr_messages)}, - ] - elif json_format: + if llm_model.startswith("o") or parallel_tool_calls is None: + parallel_tool_calls = openai.NOT_GIVEN + + if json_format: response_format = {"type": "json_object"} response = await oai_client.chat.completions.create( @@ -374,9 +372,7 @@ async def llm_call( response_format=response_format, # type: ignore max_completion_tokens=max_tokens, tools=tools_param, # type: ignore - parallel_tool_calls=( - openai.NOT_GIVEN if parallel_tool_calls is None else parallel_tool_calls - ), + parallel_tool_calls=parallel_tool_calls, ) if response.choices[0].message.tool_calls: @@ -699,7 +695,11 @@ class AIStructuredResponseGeneratorBlock(AIBlockBase): default=None, description="The maximum number of tokens to generate in the chat completion.", ) - + compress_prompt_to_fit: bool = SchemaField( + advanced=True, + default=True, + description="Whether to compress the prompt to fit within the model's context window.", + ) ollama_host: str = SchemaField( advanced=True, default="localhost:11434", @@ -757,6 +757,7 @@ class AIStructuredResponseGeneratorBlock(AIBlockBase): llm_model: LlmModel, prompt: list[dict], json_format: bool, + compress_prompt_to_fit: bool, max_tokens: int | None, tools: list[dict] | None = None, ollama_host: str = "localhost:11434", @@ -774,6 +775,7 @@ class AIStructuredResponseGeneratorBlock(AIBlockBase): max_tokens=max_tokens, tools=tools, ollama_host=ollama_host, + compress_prompt_to_fit=compress_prompt_to_fit, ) async def run( @@ -832,7 +834,7 @@ class AIStructuredResponseGeneratorBlock(AIBlockBase): except JSONDecodeError as e: return f"JSON decode error: {e}" - logger.info(f"LLM request: {prompt}") + logger.debug(f"LLM request: {prompt}") retry_prompt = "" llm_model = input_data.model @@ -842,6 +844,7 @@ class AIStructuredResponseGeneratorBlock(AIBlockBase): credentials=credentials, llm_model=llm_model, prompt=prompt, + compress_prompt_to_fit=input_data.compress_prompt_to_fit, json_format=bool(input_data.expected_format), ollama_host=input_data.ollama_host, max_tokens=input_data.max_tokens, @@ -853,7 +856,7 @@ class AIStructuredResponseGeneratorBlock(AIBlockBase): output_token_count=llm_response.completion_tokens, ) ) - logger.info(f"LLM attempt-{retry_count} response: {response_text}") + logger.debug(f"LLM attempt-{retry_count} response: {response_text}") if input_data.expected_format: diff --git a/autogpt_platform/backend/backend/blocks/mem0.py b/autogpt_platform/backend/backend/blocks/mem0.py index ad2c64f8f0..9ce11e6a73 100644 --- a/autogpt_platform/backend/backend/blocks/mem0.py +++ b/autogpt_platform/backend/backend/blocks/mem0.py @@ -13,7 +13,7 @@ from backend.data.model import ( from backend.integrations.providers import ProviderName TEST_CREDENTIALS = APIKeyCredentials( - id="ed55ac19-356e-4243-a6cb-bc599e9b716f", + id="8cc8b2c5-d3e4-4b1c-84ad-e1e9fe2a0122", provider="mem0", api_key=SecretStr("mock-mem0-api-key"), title="Mock Mem0 API key", diff --git a/autogpt_platform/backend/backend/blocks/smart_decision_maker.py b/autogpt_platform/backend/backend/blocks/smart_decision_maker.py index 2fe43af806..4d7116af90 100644 --- a/autogpt_platform/backend/backend/blocks/smart_decision_maker.py +++ b/autogpt_platform/backend/backend/blocks/smart_decision_maker.py @@ -85,7 +85,7 @@ def _get_tool_responses(entry: dict[str, Any]) -> list[str]: return tool_call_ids -def _create_tool_response(call_id: str, output: dict[str, Any]) -> dict[str, Any]: +def _create_tool_response(call_id: str, output: Any) -> dict[str, Any]: """ Create a tool response message for either OpenAI or Anthropics, based on the tool_id format. @@ -212,6 +212,15 @@ class SmartDecisionMakerBlock(Block): "link like the output of `StoreValue` or `AgentInput` block" ) + # Check that both conversation_history and last_tool_output are connected together + if any(link.sink_name == "conversation_history" for link in links) != any( + link.sink_name == "last_tool_output" for link in links + ): + raise ValueError( + "Last Tool Output is needed when Conversation History is used, " + "and vice versa. Please connect both inputs together." + ) + return missing_links @classmethod @@ -222,8 +231,15 @@ class SmartDecisionMakerBlock(Block): conversation_history = data.get("conversation_history", []) pending_tool_calls = get_pending_tool_calls(conversation_history) last_tool_output = data.get("last_tool_output") - if not last_tool_output and pending_tool_calls: + + # Tool call is pending, wait for the tool output to be provided. + if last_tool_output is None and pending_tool_calls: return {"last_tool_output"} + + # No tool call is pending, wait for the conversation history to be updated. + if last_tool_output is not None and not pending_tool_calls: + return {"conversation_history"} + return set() class Output(BlockSchema): @@ -433,7 +449,7 @@ class SmartDecisionMakerBlock(Block): prompt = [json.to_dict(p) for p in input_data.conversation_history if p] pending_tool_calls = get_pending_tool_calls(input_data.conversation_history) - if pending_tool_calls and not input_data.last_tool_output: + if pending_tool_calls and input_data.last_tool_output is None: raise ValueError(f"Tool call requires an output for {pending_tool_calls}") # Prefill all missing tool calls with the last tool output/ @@ -497,7 +513,7 @@ class SmartDecisionMakerBlock(Block): max_tokens=input_data.max_tokens, tools=tool_functions, ollama_host=input_data.ollama_host, - parallel_tool_calls=True if input_data.multiple_tool_calls else None, + parallel_tool_calls=input_data.multiple_tool_calls, ) if not response.tool_calls: diff --git a/autogpt_platform/backend/backend/blocks/smartlead/campaign.py b/autogpt_platform/backend/backend/blocks/smartlead/campaign.py index 0e6c72416b..112004d7ad 100644 --- a/autogpt_platform/backend/backend/blocks/smartlead/campaign.py +++ b/autogpt_platform/backend/backend/blocks/smartlead/campaign.py @@ -17,7 +17,7 @@ from backend.blocks.smartlead.models import ( Sequence, ) from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema -from backend.data.model import SchemaField +from backend.data.model import CredentialsField, SchemaField class CreateCampaignBlock(Block): @@ -27,7 +27,7 @@ class CreateCampaignBlock(Block): name: str = SchemaField( description="The name of the campaign", ) - credentials: SmartLeadCredentialsInput = SchemaField( + credentials: SmartLeadCredentialsInput = CredentialsField( description="SmartLead credentials", ) @@ -119,7 +119,7 @@ class AddLeadToCampaignBlock(Block): description="Settings for lead upload", default=LeadUploadSettings(), ) - credentials: SmartLeadCredentialsInput = SchemaField( + credentials: SmartLeadCredentialsInput = CredentialsField( description="SmartLead credentials", ) @@ -251,7 +251,7 @@ class SaveCampaignSequencesBlock(Block): default_factory=list, advanced=False, ) - credentials: SmartLeadCredentialsInput = SchemaField( + credentials: SmartLeadCredentialsInput = CredentialsField( description="SmartLead credentials", ) diff --git a/autogpt_platform/backend/test/block/test_block.py b/autogpt_platform/backend/backend/blocks/test/test_block.py similarity index 100% rename from autogpt_platform/backend/test/block/test_block.py rename to autogpt_platform/backend/backend/blocks/test/test_block.py diff --git a/autogpt_platform/backend/backend/blocks/test/test_http.py b/autogpt_platform/backend/backend/blocks/test/test_http.py new file mode 100644 index 0000000000..ad0dd99b4c --- /dev/null +++ b/autogpt_platform/backend/backend/blocks/test/test_http.py @@ -0,0 +1,485 @@ +"""Comprehensive tests for HTTP block with HostScopedCredentials functionality.""" + +from typing import cast +from unittest.mock import AsyncMock, MagicMock, patch + +import pytest +from pydantic import SecretStr + +from backend.blocks.http import ( + HttpCredentials, + HttpMethod, + SendAuthenticatedWebRequestBlock, +) +from backend.data.model import HostScopedCredentials +from backend.util.request import Response + + +class TestHttpBlockWithHostScopedCredentials: + """Test suite for HTTP block integration with HostScopedCredentials.""" + + @pytest.fixture + def http_block(self): + """Create an HTTP block instance.""" + return SendAuthenticatedWebRequestBlock() + + @pytest.fixture + def mock_response(self): + """Mock a successful HTTP response.""" + response = MagicMock(spec=Response) + response.status = 200 + response.headers = {"content-type": "application/json"} + response.json.return_value = {"success": True, "data": "test"} + return response + + @pytest.fixture + def exact_match_credentials(self): + """Create host-scoped credentials for exact domain matching.""" + return HostScopedCredentials( + provider="http", + host="api.example.com", + headers={ + "Authorization": SecretStr("Bearer exact-match-token"), + "X-API-Key": SecretStr("api-key-123"), + }, + title="Exact Match API Credentials", + ) + + @pytest.fixture + def wildcard_credentials(self): + """Create host-scoped credentials with wildcard pattern.""" + return HostScopedCredentials( + provider="http", + host="*.github.com", + headers={ + "Authorization": SecretStr("token ghp_wildcard123"), + }, + title="GitHub Wildcard Credentials", + ) + + @pytest.fixture + def non_matching_credentials(self): + """Create credentials that don't match test URLs.""" + return HostScopedCredentials( + provider="http", + host="different.api.com", + headers={ + "Authorization": SecretStr("Bearer non-matching-token"), + }, + title="Non-matching Credentials", + ) + + @pytest.mark.asyncio + @patch("backend.blocks.http.Requests") + async def test_http_block_with_exact_host_match( + self, + mock_requests_class, + http_block, + exact_match_credentials, + mock_response, + ): + """Test HTTP block with exact host matching credentials.""" + # Setup mocks + mock_requests = AsyncMock() + mock_requests.request.return_value = mock_response + mock_requests_class.return_value = mock_requests + + # Prepare input data + input_data = SendAuthenticatedWebRequestBlock.Input( + url="https://api.example.com/data", + method=HttpMethod.GET, + headers={"User-Agent": "test-agent"}, + credentials=cast( + HttpCredentials, + { + "id": exact_match_credentials.id, + "provider": "http", + "type": "host_scoped", + "title": exact_match_credentials.title, + }, + ), + ) + + # Execute with credentials provided by execution manager + result = [] + async for output_name, output_data in http_block.run( + input_data, + credentials=exact_match_credentials, + graph_exec_id="test-exec-id", + ): + result.append((output_name, output_data)) + + # Verify request headers include both credential and user headers + mock_requests.request.assert_called_once() + call_args = mock_requests.request.call_args + expected_headers = { + "Authorization": "Bearer exact-match-token", + "X-API-Key": "api-key-123", + "User-Agent": "test-agent", + } + assert call_args.kwargs["headers"] == expected_headers + + # Verify response handling + assert len(result) == 1 + assert result[0][0] == "response" + assert result[0][1] == {"success": True, "data": "test"} + + @pytest.mark.asyncio + @patch("backend.blocks.http.Requests") + async def test_http_block_with_wildcard_host_match( + self, + mock_requests_class, + http_block, + wildcard_credentials, + mock_response, + ): + """Test HTTP block with wildcard host pattern matching.""" + # Setup mocks + mock_requests = AsyncMock() + mock_requests.request.return_value = mock_response + mock_requests_class.return_value = mock_requests + + # Test with subdomain that should match *.github.com + input_data = SendAuthenticatedWebRequestBlock.Input( + url="https://api.github.com/user", + method=HttpMethod.GET, + headers={}, + credentials=cast( + HttpCredentials, + { + "id": wildcard_credentials.id, + "provider": "http", + "type": "host_scoped", + "title": wildcard_credentials.title, + }, + ), + ) + + # Execute with wildcard credentials + result = [] + async for output_name, output_data in http_block.run( + input_data, + credentials=wildcard_credentials, + graph_exec_id="test-exec-id", + ): + result.append((output_name, output_data)) + + # Verify wildcard matching works + mock_requests.request.assert_called_once() + call_args = mock_requests.request.call_args + expected_headers = {"Authorization": "token ghp_wildcard123"} + assert call_args.kwargs["headers"] == expected_headers + + @pytest.mark.asyncio + @patch("backend.blocks.http.Requests") + async def test_http_block_with_non_matching_credentials( + self, + mock_requests_class, + http_block, + non_matching_credentials, + mock_response, + ): + """Test HTTP block when credentials don't match the target URL.""" + # Setup mocks + mock_requests = AsyncMock() + mock_requests.request.return_value = mock_response + mock_requests_class.return_value = mock_requests + + # Test with URL that doesn't match the credentials + input_data = SendAuthenticatedWebRequestBlock.Input( + url="https://api.example.com/data", + method=HttpMethod.GET, + headers={"User-Agent": "test-agent"}, + credentials=cast( + HttpCredentials, + { + "id": non_matching_credentials.id, + "provider": "http", + "type": "host_scoped", + "title": non_matching_credentials.title, + }, + ), + ) + + # Execute with non-matching credentials + result = [] + async for output_name, output_data in http_block.run( + input_data, + credentials=non_matching_credentials, + graph_exec_id="test-exec-id", + ): + result.append((output_name, output_data)) + + # Verify only user headers are included (no credential headers) + mock_requests.request.assert_called_once() + call_args = mock_requests.request.call_args + expected_headers = {"User-Agent": "test-agent"} + assert call_args.kwargs["headers"] == expected_headers + + @pytest.mark.asyncio + @patch("backend.blocks.http.Requests") + async def test_user_headers_override_credential_headers( + self, + mock_requests_class, + http_block, + exact_match_credentials, + mock_response, + ): + """Test that user-provided headers take precedence over credential headers.""" + # Setup mocks + mock_requests = AsyncMock() + mock_requests.request.return_value = mock_response + mock_requests_class.return_value = mock_requests + + # Test with user header that conflicts with credential header + input_data = SendAuthenticatedWebRequestBlock.Input( + url="https://api.example.com/data", + method=HttpMethod.POST, + headers={ + "Authorization": "Bearer user-override-token", # Should override + "Content-Type": "application/json", # Additional user header + }, + credentials=cast( + HttpCredentials, + { + "id": exact_match_credentials.id, + "provider": "http", + "type": "host_scoped", + "title": exact_match_credentials.title, + }, + ), + ) + + # Execute with conflicting headers + result = [] + async for output_name, output_data in http_block.run( + input_data, + credentials=exact_match_credentials, + graph_exec_id="test-exec-id", + ): + result.append((output_name, output_data)) + + # Verify user headers take precedence + mock_requests.request.assert_called_once() + call_args = mock_requests.request.call_args + expected_headers = { + "X-API-Key": "api-key-123", # From credentials + "Authorization": "Bearer user-override-token", # User override + "Content-Type": "application/json", # User header + } + assert call_args.kwargs["headers"] == expected_headers + + @pytest.mark.asyncio + @patch("backend.blocks.http.Requests") + async def test_auto_discovered_credentials_flow( + self, + mock_requests_class, + http_block, + mock_response, + ): + """Test the auto-discovery flow where execution manager provides matching credentials.""" + # Create auto-discovered credentials + auto_discovered_creds = HostScopedCredentials( + provider="http", + host="*.example.com", + headers={ + "Authorization": SecretStr("Bearer auto-discovered-token"), + }, + title="Auto-discovered Credentials", + ) + + # Setup mocks + mock_requests = AsyncMock() + mock_requests.request.return_value = mock_response + mock_requests_class.return_value = mock_requests + + # Test with empty credentials field (triggers auto-discovery) + input_data = SendAuthenticatedWebRequestBlock.Input( + url="https://api.example.com/data", + method=HttpMethod.GET, + headers={}, + credentials=cast( + HttpCredentials, + { + "id": "", # Empty ID triggers auto-discovery in execution manager + "provider": "http", + "type": "host_scoped", + "title": "", + }, + ), + ) + + # Execute with auto-discovered credentials provided by execution manager + result = [] + async for output_name, output_data in http_block.run( + input_data, + credentials=auto_discovered_creds, # Execution manager found these + graph_exec_id="test-exec-id", + ): + result.append((output_name, output_data)) + + # Verify auto-discovered credentials were applied + mock_requests.request.assert_called_once() + call_args = mock_requests.request.call_args + expected_headers = {"Authorization": "Bearer auto-discovered-token"} + assert call_args.kwargs["headers"] == expected_headers + + # Verify response handling + assert len(result) == 1 + assert result[0][0] == "response" + assert result[0][1] == {"success": True, "data": "test"} + + @pytest.mark.asyncio + @patch("backend.blocks.http.Requests") + async def test_multiple_header_credentials( + self, + mock_requests_class, + http_block, + mock_response, + ): + """Test credentials with multiple headers are all applied.""" + # Create credentials with multiple headers + multi_header_creds = HostScopedCredentials( + provider="http", + host="api.example.com", + headers={ + "Authorization": SecretStr("Bearer multi-token"), + "X-API-Key": SecretStr("api-key-456"), + "X-Client-ID": SecretStr("client-789"), + "X-Custom-Header": SecretStr("custom-value"), + }, + title="Multi-Header Credentials", + ) + + # Setup mocks + mock_requests = AsyncMock() + mock_requests.request.return_value = mock_response + mock_requests_class.return_value = mock_requests + + # Test with credentials containing multiple headers + input_data = SendAuthenticatedWebRequestBlock.Input( + url="https://api.example.com/data", + method=HttpMethod.GET, + headers={"User-Agent": "test-agent"}, + credentials=cast( + HttpCredentials, + { + "id": multi_header_creds.id, + "provider": "http", + "type": "host_scoped", + "title": multi_header_creds.title, + }, + ), + ) + + # Execute with multi-header credentials + result = [] + async for output_name, output_data in http_block.run( + input_data, + credentials=multi_header_creds, + graph_exec_id="test-exec-id", + ): + result.append((output_name, output_data)) + + # Verify all headers are included + mock_requests.request.assert_called_once() + call_args = mock_requests.request.call_args + expected_headers = { + "Authorization": "Bearer multi-token", + "X-API-Key": "api-key-456", + "X-Client-ID": "client-789", + "X-Custom-Header": "custom-value", + "User-Agent": "test-agent", + } + assert call_args.kwargs["headers"] == expected_headers + + @pytest.mark.asyncio + @patch("backend.blocks.http.Requests") + async def test_credentials_with_complex_url_patterns( + self, + mock_requests_class, + http_block, + mock_response, + ): + """Test credentials matching various URL patterns.""" + # Test cases for different URL patterns + test_cases = [ + { + "host_pattern": "api.example.com", + "test_url": "https://api.example.com/v1/users", + "should_match": True, + }, + { + "host_pattern": "*.example.com", + "test_url": "https://api.example.com/v1/users", + "should_match": True, + }, + { + "host_pattern": "*.example.com", + "test_url": "https://subdomain.example.com/data", + "should_match": True, + }, + { + "host_pattern": "api.example.com", + "test_url": "https://api.different.com/data", + "should_match": False, + }, + ] + + # Setup mocks + mock_requests = AsyncMock() + mock_requests.request.return_value = mock_response + mock_requests_class.return_value = mock_requests + + for case in test_cases: + # Reset mock for each test case + mock_requests.reset_mock() + + # Create credentials for this test case + test_creds = HostScopedCredentials( + provider="http", + host=case["host_pattern"], + headers={ + "Authorization": SecretStr(f"Bearer {case['host_pattern']}-token"), + }, + title=f"Credentials for {case['host_pattern']}", + ) + + input_data = SendAuthenticatedWebRequestBlock.Input( + url=case["test_url"], + method=HttpMethod.GET, + headers={"User-Agent": "test-agent"}, + credentials=cast( + HttpCredentials, + { + "id": test_creds.id, + "provider": "http", + "type": "host_scoped", + "title": test_creds.title, + }, + ), + ) + + # Execute with test credentials + result = [] + async for output_name, output_data in http_block.run( + input_data, + credentials=test_creds, + graph_exec_id="test-exec-id", + ): + result.append((output_name, output_data)) + + # Verify headers based on whether pattern should match + mock_requests.request.assert_called_once() + call_args = mock_requests.request.call_args + headers = call_args.kwargs["headers"] + + if case["should_match"]: + # Should include both user and credential headers + expected_auth = f"Bearer {case['host_pattern']}-token" + assert headers["Authorization"] == expected_auth + assert headers["User-Agent"] == "test-agent" + else: + # Should only include user headers + assert "Authorization" not in headers + assert headers["User-Agent"] == "test-agent" diff --git a/autogpt_platform/backend/test/executor/test_tool_use.py b/autogpt_platform/backend/backend/blocks/test/test_smart_decision_maker.py similarity index 72% rename from autogpt_platform/backend/test/executor/test_tool_use.py rename to autogpt_platform/backend/backend/blocks/test/test_smart_decision_maker.py index 54ecfbce0a..336753a1d2 100644 --- a/autogpt_platform/backend/test/executor/test_tool_use.py +++ b/autogpt_platform/backend/backend/blocks/test/test_smart_decision_maker.py @@ -25,12 +25,7 @@ async def create_graph(s: SpinTestServer, g: graph.Graph, u: User) -> graph.Grap async def create_credentials(s: SpinTestServer, u: User): provider = ProviderName.OPENAI credentials = llm.TEST_CREDENTIALS - try: - await s.agent_server.test_create_credentials(u.id, provider, credentials) - except Exception: - # ValueErrors is raised trying to recreate the same credentials - # so hidding the error - pass + return await s.agent_server.test_create_credentials(u.id, provider, credentials) async def execute_graph( @@ -60,19 +55,18 @@ async def execute_graph( return graph_exec_id -@pytest.mark.skip() @pytest.mark.asyncio(loop_scope="session") async def test_graph_validation_with_tool_nodes_correct(server: SpinTestServer): test_user = await create_test_user() test_tool_graph = await create_graph(server, create_test_graph(), test_user) - await create_credentials(server, test_user) + creds = await create_credentials(server, test_user) nodes = [ graph.Node( block_id=SmartDecisionMakerBlock().id, input_default={ "prompt": "Hello, World!", - "credentials": llm.TEST_CREDENTIALS_INPUT, + "credentials": creds, }, ), graph.Node( @@ -110,80 +104,18 @@ async def test_graph_validation_with_tool_nodes_correct(server: SpinTestServer): test_graph = await create_graph(server, test_graph, test_user) -@pytest.mark.skip() -@pytest.mark.asyncio(loop_scope="session") -async def test_graph_validation_with_tool_nodes_raises_error(server: SpinTestServer): - - test_user = await create_test_user() - test_tool_graph = await create_graph(server, create_test_graph(), test_user) - await create_credentials(server, test_user) - - nodes = [ - graph.Node( - block_id=SmartDecisionMakerBlock().id, - input_default={ - "prompt": "Hello, World!", - "credentials": llm.TEST_CREDENTIALS_INPUT, - }, - ), - graph.Node( - block_id=AgentExecutorBlock().id, - input_default={ - "graph_id": test_tool_graph.id, - "graph_version": test_tool_graph.version, - "input_schema": test_tool_graph.input_schema, - "output_schema": test_tool_graph.output_schema, - }, - ), - graph.Node( - block_id=StoreValueBlock().id, - ), - ] - - links = [ - graph.Link( - source_id=nodes[0].id, - sink_id=nodes[1].id, - source_name="tools_^_sample_tool_input_1", - sink_name="input_1", - ), - graph.Link( - source_id=nodes[0].id, - sink_id=nodes[1].id, - source_name="tools_^_sample_tool_input_2", - sink_name="input_2", - ), - graph.Link( - source_id=nodes[0].id, - sink_id=nodes[2].id, - source_name="tools_^_store_value_input", - sink_name="input", - ), - ] - - test_graph = graph.Graph( - name="TestGraph", - description="Test graph", - nodes=nodes, - links=links, - ) - with pytest.raises(ValueError): - test_graph = await create_graph(server, test_graph, test_user) - - -@pytest.mark.skip() @pytest.mark.asyncio(loop_scope="session") async def test_smart_decision_maker_function_signature(server: SpinTestServer): test_user = await create_test_user() test_tool_graph = await create_graph(server, create_test_graph(), test_user) - await create_credentials(server, test_user) + creds = await create_credentials(server, test_user) nodes = [ graph.Node( block_id=SmartDecisionMakerBlock().id, input_default={ "prompt": "Hello, World!", - "credentials": llm.TEST_CREDENTIALS_INPUT, + "credentials": creds, }, ), graph.Node( diff --git a/autogpt_platform/backend/backend/blocks/zerobounce/validate_emails.py b/autogpt_platform/backend/backend/blocks/zerobounce/validate_emails.py index b23e822ddc..6bb96d0a8f 100644 --- a/autogpt_platform/backend/backend/blocks/zerobounce/validate_emails.py +++ b/autogpt_platform/backend/backend/blocks/zerobounce/validate_emails.py @@ -15,7 +15,7 @@ from backend.blocks.zerobounce._auth import ( ZeroBounceCredentialsInput, ) from backend.data.block import Block, BlockCategory, BlockOutput, BlockSchema -from backend.data.model import SchemaField +from backend.data.model import CredentialsField, SchemaField class Response(BaseModel): @@ -90,7 +90,7 @@ class ValidateEmailsBlock(Block): description="IP address to validate", default="", ) - credentials: ZeroBounceCredentialsInput = SchemaField( + credentials: ZeroBounceCredentialsInput = CredentialsField( description="ZeroBounce credentials", ) diff --git a/autogpt_platform/backend/test/conftest.py b/autogpt_platform/backend/backend/conftest.py similarity index 97% rename from autogpt_platform/backend/test/conftest.py rename to autogpt_platform/backend/backend/conftest.py index b0b7f0cc67..d73ed7712c 100644 --- a/autogpt_platform/backend/test/conftest.py +++ b/autogpt_platform/backend/backend/conftest.py @@ -6,6 +6,8 @@ from dotenv import load_dotenv from backend.util.logging import configure_logging +os.environ["ENABLE_AUTH"] = "false" + load_dotenv() # NOTE: You can run tests like with the --log-cli-level=INFO to see the logs diff --git a/autogpt_platform/backend/backend/data/__init__.py b/autogpt_platform/backend/backend/data/__init__.py new file mode 100644 index 0000000000..7cbc4487be --- /dev/null +++ b/autogpt_platform/backend/backend/data/__init__.py @@ -0,0 +1,5 @@ +from .graph import NodeModel +from .integrations import Webhook # noqa: F401 + +# Resolve Webhook <- NodeModel forward reference +NodeModel.model_rebuild() diff --git a/autogpt_platform/backend/backend/data/block.py b/autogpt_platform/backend/backend/data/block.py index 5283997d4c..f5e9e8b1cd 100644 --- a/autogpt_platform/backend/backend/data/block.py +++ b/autogpt_platform/backend/backend/data/block.py @@ -78,6 +78,7 @@ class BlockCategory(Enum): PRODUCTIVITY = "Block that helps with productivity" ISSUE_TRACKING = "Block that helps with issue tracking" MULTIMEDIA = "Block that interacts with multimedia content" + MARKETING = "Block that helps with marketing" def dict(self) -> dict[str, str]: return {"category": self.name, "description": self.value} @@ -485,6 +486,22 @@ class Block(ABC, Generic[BlockSchemaInputType, BlockSchemaOutputType]): raise ValueError(f"Block produced an invalid output data: {error}") yield output_name, output_data + def is_triggered_by_event_type( + self, trigger_config: dict[str, Any], event_type: str + ) -> bool: + if not self.webhook_config: + raise TypeError("This method can't be used on non-trigger blocks") + if not self.webhook_config.event_filter_input: + return True + event_filter = trigger_config.get(self.webhook_config.event_filter_input) + if not event_filter: + raise ValueError("Event filter is not configured on trigger") + return event_type in [ + self.webhook_config.event_format.format(event=k) + for k in event_filter + if event_filter[k] is True + ] + # ======================= Block Helper Functions ======================= # diff --git a/autogpt_platform/backend/backend/data/block_cost_config.py b/autogpt_platform/backend/backend/data/block_cost_config.py index 730512e4ea..4ef6703773 100644 --- a/autogpt_platform/backend/backend/data/block_cost_config.py +++ b/autogpt_platform/backend/backend/data/block_cost_config.py @@ -4,6 +4,7 @@ from backend.blocks.ai_music_generator import AIMusicGeneratorBlock from backend.blocks.ai_shortform_video_block import AIShortformVideoCreatorBlock from backend.blocks.apollo.organization import SearchOrganizationsBlock from backend.blocks.apollo.people import SearchPeopleBlock +from backend.blocks.apollo.person import GetPersonDetailBlock from backend.blocks.flux_kontext import AIImageEditorBlock, FluxKontextModelName from backend.blocks.ideogram import IdeogramModelBlock from backend.blocks.jina.embeddings import JinaEmbeddingBlock @@ -362,7 +363,31 @@ BLOCK_COSTS: dict[Type[Block], list[BlockCost]] = { ], SearchPeopleBlock: [ BlockCost( - cost_amount=2, + cost_amount=10, + cost_filter={ + "enrich_info": False, + "credentials": { + "id": apollo_credentials.id, + "provider": apollo_credentials.provider, + "type": apollo_credentials.type, + }, + }, + ), + BlockCost( + cost_amount=20, + cost_filter={ + "enrich_info": True, + "credentials": { + "id": apollo_credentials.id, + "provider": apollo_credentials.provider, + "type": apollo_credentials.type, + }, + }, + ), + ], + GetPersonDetailBlock: [ + BlockCost( + cost_amount=1, cost_filter={ "credentials": { "id": apollo_credentials.id, diff --git a/autogpt_platform/backend/test/data/test_credit.py b/autogpt_platform/backend/backend/data/credit_test.py similarity index 100% rename from autogpt_platform/backend/test/data/test_credit.py rename to autogpt_platform/backend/backend/data/credit_test.py diff --git a/autogpt_platform/backend/backend/data/queue.py b/autogpt_platform/backend/backend/data/event_bus.py similarity index 98% rename from autogpt_platform/backend/backend/data/queue.py rename to autogpt_platform/backend/backend/data/event_bus.py index 2e2710ef07..5e12ace471 100644 --- a/autogpt_platform/backend/backend/data/queue.py +++ b/autogpt_platform/backend/backend/data/event_bus.py @@ -7,7 +7,7 @@ from pydantic import BaseModel from redis.asyncio.client import PubSub as AsyncPubSub from redis.client import PubSub -from backend.data import redis +from backend.data import redis_client as redis logger = logging.getLogger(__name__) diff --git a/autogpt_platform/backend/backend/data/execution.py b/autogpt_platform/backend/backend/data/execution.py index e97bb30452..090d120580 100644 --- a/autogpt_platform/backend/backend/data/execution.py +++ b/autogpt_platform/backend/backend/data/execution.py @@ -32,7 +32,7 @@ from prisma.types import ( AgentNodeExecutionUpdateInput, AgentNodeExecutionWhereInput, ) -from pydantic import BaseModel, ConfigDict +from pydantic import BaseModel, ConfigDict, JsonValue from pydantic.fields import Field from backend.server.v2.store.exceptions import DatabaseError @@ -48,14 +48,14 @@ from .block import ( get_webhook_block_ids, ) from .db import BaseDbModel +from .event_bus import AsyncRedisEventBus, RedisEventBus from .includes import ( EXECUTION_RESULT_INCLUDE, EXECUTION_RESULT_ORDER, GRAPH_EXECUTION_INCLUDE_WITH_NODES, graph_execution_include, ) -from .model import CredentialsMetaInput, GraphExecutionStats, NodeExecutionStats -from .queue import AsyncRedisEventBus, RedisEventBus +from .model import GraphExecutionStats, NodeExecutionStats T = TypeVar("T") @@ -271,7 +271,7 @@ class GraphExecutionWithNodes(GraphExecution): graph_id=self.graph_id, graph_version=self.graph_version or 0, graph_exec_id=self.id, - node_credentials_input_map={}, # FIXME + nodes_input_masks={}, # FIXME: store credentials on AgentGraphExecution ) @@ -588,12 +588,10 @@ async def update_graph_execution_start_time( async def update_graph_execution_stats( graph_exec_id: str, - status: ExecutionStatus, + status: ExecutionStatus | None = None, stats: GraphExecutionStats | None = None, ) -> GraphExecution | None: - update_data: AgentGraphExecutionUpdateManyMutationInput = { - "executionStatus": status - } + update_data: AgentGraphExecutionUpdateManyMutationInput = {} if stats: stats_dict = stats.model_dump() @@ -601,6 +599,9 @@ async def update_graph_execution_stats( stats_dict["error"] = str(stats_dict["error"]) update_data["stats"] = Json(stats_dict) + if status: + update_data["executionStatus"] = status + updated_count = await AgentGraphExecution.prisma().update_many( where={ "id": graph_exec_id, @@ -783,7 +784,7 @@ class GraphExecutionEntry(BaseModel): graph_exec_id: str graph_id: str graph_version: int - node_credentials_input_map: Optional[dict[str, dict[str, CredentialsMetaInput]]] + nodes_input_masks: Optional[dict[str, dict[str, JsonValue]]] = None class NodeExecutionEntry(BaseModel): diff --git a/autogpt_platform/backend/backend/data/graph.py b/autogpt_platform/backend/backend/data/graph.py index d3d3ae8a02..215a3f1c92 100644 --- a/autogpt_platform/backend/backend/data/graph.py +++ b/autogpt_platform/backend/backend/data/graph.py @@ -1,7 +1,7 @@ import logging import uuid from collections import defaultdict -from typing import Any, Literal, Optional, cast +from typing import TYPE_CHECKING, Any, Literal, Optional, cast import prisma from prisma import Json @@ -14,7 +14,7 @@ from prisma.types import ( AgentNodeLinkCreateInput, StoreListingVersionWhereInput, ) -from pydantic import create_model +from pydantic import JsonValue, create_model from pydantic.fields import computed_field from backend.blocks.agent import AgentExecutorBlock @@ -27,12 +27,15 @@ from backend.data.model import ( CredentialsMetaInput, is_credentials_field_name, ) +from backend.integrations.providers import ProviderName from backend.util import type as type_utils from .block import Block, BlockInput, BlockSchema, BlockType, get_block, get_blocks from .db import BaseDbModel, transaction from .includes import AGENT_GRAPH_INCLUDE, AGENT_NODE_INCLUDE -from .integrations import Webhook + +if TYPE_CHECKING: + from .integrations import Webhook logger = logging.getLogger(__name__) @@ -81,10 +84,12 @@ class NodeModel(Node): graph_version: int webhook_id: Optional[str] = None - webhook: Optional[Webhook] = None + webhook: Optional["Webhook"] = None @staticmethod def from_db(node: AgentNode, for_export: bool = False) -> "NodeModel": + from .integrations import Webhook + obj = NodeModel( id=node.id, block_id=node.agentBlockId, @@ -102,19 +107,7 @@ class NodeModel(Node): return obj def is_triggered_by_event_type(self, event_type: str) -> bool: - block = self.block - if not block.webhook_config: - raise TypeError("This method can't be used on non-webhook blocks") - if not block.webhook_config.event_filter_input: - return True - event_filter = self.input_default.get(block.webhook_config.event_filter_input) - if not event_filter: - raise ValueError(f"Event filter is not configured on node #{self.id}") - return event_type in [ - block.webhook_config.event_format.format(event=k) - for k in event_filter - if event_filter[k] is True - ] + return self.block.is_triggered_by_event_type(self.input_default, event_type) def stripped_for_export(self) -> "NodeModel": """ @@ -162,10 +155,6 @@ class NodeModel(Node): return result -# Fix 2-way reference Node <-> Webhook -Webhook.model_rebuild() - - class BaseGraph(BaseDbModel): version: int = 1 is_active: bool = True @@ -255,6 +244,8 @@ class Graph(BaseGraph): for other_field, other_keys in list(graph_cred_fields)[i + 1 :]: if field.provider != other_field.provider: continue + if ProviderName.HTTP in field.provider: + continue # If this happens, that means a block implementation probably needs # to be updated. @@ -276,6 +267,7 @@ class Graph(BaseGraph): required_scopes=set(field_info.required_scopes or []), discriminator=field_info.discriminator, discriminator_mapping=field_info.discriminator_mapping, + discriminator_values=field_info.discriminator_values, ), ) for agg_field_key, (field_info, _) in graph_credentials_inputs.items() @@ -294,37 +286,40 @@ class Graph(BaseGraph): Returns: dict[aggregated_field_key, tuple( CredentialsFieldInfo: A spec for one aggregated credentials field + (now includes discriminator_values from matching nodes) set[(node_id, field_name)]: Node credentials fields that are compatible with this aggregated field spec )] """ - return { - "_".join(sorted(agg_field_info.provider)) - + "_" - + "_".join(sorted(agg_field_info.supported_types)) - + "_credentials": (agg_field_info, node_fields) - for agg_field_info, node_fields in CredentialsFieldInfo.combine( - *( - ( - # Apply discrimination before aggregating credentials inputs - ( - field_info.discriminate( - node.input_default[field_info.discriminator] - ) - if ( - field_info.discriminator - and node.input_default.get(field_info.discriminator) - ) - else field_info - ), - (node.id, field_name), + # First collect all credential field data with input defaults + node_credential_data = [] + + for graph in [self] + self.sub_graphs: + for node in graph.nodes: + for ( + field_name, + field_info, + ) in node.block.input_schema.get_credentials_fields_info().items(): + + discriminator = field_info.discriminator + if not discriminator: + node_credential_data.append((field_info, (node.id, field_name))) + continue + + discriminator_value = node.input_default.get(discriminator) + if discriminator_value is None: + node_credential_data.append((field_info, (node.id, field_name))) + continue + + discriminated_info = field_info.discriminate(discriminator_value) + discriminated_info.discriminator_values.add(discriminator_value) + + node_credential_data.append( + (discriminated_info, (node.id, field_name)) ) - for graph in [self] + self.sub_graphs - for node in graph.nodes - for field_name, field_info in node.block.input_schema.get_credentials_fields_info().items() - ) - ) - } + + # Combine credential field info (this will merge discriminator_values automatically) + return CredentialsFieldInfo.combine(*node_credential_data) class GraphModel(Graph): @@ -403,16 +398,26 @@ class GraphModel(Graph): continue node.input_default["user_id"] = user_id node.input_default.setdefault("inputs", {}) - if (graph_id := node.input_default.get("graph_id")) in graph_id_map: + if ( + graph_id := node.input_default.get("graph_id") + ) and graph_id in graph_id_map: node.input_default["graph_id"] = graph_id_map[graph_id] - def validate_graph(self, for_run: bool = False): - self._validate_graph(self, for_run) + def validate_graph( + self, + for_run: bool = False, + nodes_input_masks: Optional[dict[str, dict[str, JsonValue]]] = None, + ): + self._validate_graph(self, for_run, nodes_input_masks) for sub_graph in self.sub_graphs: - self._validate_graph(sub_graph, for_run) + self._validate_graph(sub_graph, for_run, nodes_input_masks) @staticmethod - def _validate_graph(graph: BaseGraph, for_run: bool = False): + def _validate_graph( + graph: BaseGraph, + for_run: bool = False, + nodes_input_masks: Optional[dict[str, dict[str, JsonValue]]] = None, + ): def is_tool_pin(name: str) -> bool: return name.startswith("tools_^_") @@ -439,20 +444,18 @@ class GraphModel(Graph): if (block := nodes_block.get(node.id)) is None: raise ValueError(f"Invalid block {node.block_id} for node #{node.id}") + node_input_mask = ( + nodes_input_masks.get(node.id, {}) if nodes_input_masks else {} + ) provided_inputs = set( [sanitize(name) for name in node.input_default] + [sanitize(link.sink_name) for link in input_links.get(node.id, [])] + + ([name for name in node_input_mask] if node_input_mask else []) ) InputSchema = block.input_schema for name in (required_fields := InputSchema.get_required_fields()): if ( name not in provided_inputs - # Webhook payload is passed in by ExecutionManager - and not ( - name == "payload" - and block.block_type - in (BlockType.WEBHOOK, BlockType.WEBHOOK_MANUAL) - ) # Checking availability of credentials is done by ExecutionManager and name not in InputSchema.get_credentials_fields() # Validate only I/O nodes, or validate everything when executing @@ -485,10 +488,18 @@ class GraphModel(Graph): def has_value(node: Node, name: str): return ( - name in node.input_default - and node.input_default[name] is not None - and str(node.input_default[name]).strip() != "" - ) or (name in input_fields and input_fields[name].default is not None) + ( + name in node.input_default + and node.input_default[name] is not None + and str(node.input_default[name]).strip() != "" + ) + or (name in input_fields and input_fields[name].default is not None) + or ( + name in node_input_mask + and node_input_mask[name] is not None + and str(node_input_mask[name]).strip() != "" + ) + ) # Validate dependencies between fields for field_name in input_fields.keys(): @@ -574,7 +585,7 @@ class GraphModel(Graph): graph: AgentGraph, for_export: bool = False, sub_graphs: list[AgentGraph] | None = None, - ): + ) -> "GraphModel": return GraphModel( id=graph.id, user_id=graph.userId if not for_export else "", @@ -603,6 +614,7 @@ class GraphModel(Graph): async def get_node(node_id: str) -> NodeModel: + """⚠️ No `user_id` check: DO NOT USE without check in user-facing endpoints.""" node = await AgentNode.prisma().find_unique_or_raise( where={"id": node_id}, include=AGENT_NODE_INCLUDE, @@ -611,6 +623,7 @@ async def get_node(node_id: str) -> NodeModel: async def set_node_webhook(node_id: str, webhook_id: str | None) -> NodeModel: + """⚠️ No `user_id` check: DO NOT USE without check in user-facing endpoints.""" node = await AgentNode.prisma().update( where={"id": node_id}, data=( diff --git a/autogpt_platform/backend/test/data/test_graph.py b/autogpt_platform/backend/backend/data/graph_test.py similarity index 100% rename from autogpt_platform/backend/test/data/test_graph.py rename to autogpt_platform/backend/backend/data/graph_test.py diff --git a/autogpt_platform/backend/backend/data/includes.py b/autogpt_platform/backend/backend/data/includes.py index 347bb9ef91..aec4f36673 100644 --- a/autogpt_platform/backend/backend/data/includes.py +++ b/autogpt_platform/backend/backend/data/includes.py @@ -60,7 +60,8 @@ def graph_execution_include( INTEGRATION_WEBHOOK_INCLUDE: prisma.types.IntegrationWebhookInclude = { - "AgentNodes": {"include": AGENT_NODE_INCLUDE} + "AgentNodes": {"include": AGENT_NODE_INCLUDE}, + "AgentPresets": {"include": {"InputPresets": True}}, } diff --git a/autogpt_platform/backend/backend/data/integrations.py b/autogpt_platform/backend/backend/data/integrations.py index ea24a7d533..7bc4b3d93f 100644 --- a/autogpt_platform/backend/backend/data/integrations.py +++ b/autogpt_platform/backend/backend/data/integrations.py @@ -1,21 +1,25 @@ import logging -from typing import TYPE_CHECKING, AsyncGenerator, Optional +from typing import AsyncGenerator, Literal, Optional, overload from prisma import Json from prisma.models import IntegrationWebhook -from prisma.types import IntegrationWebhookCreateInput +from prisma.types import ( + IntegrationWebhookCreateInput, + IntegrationWebhookUpdateInput, + IntegrationWebhookWhereInput, + Serializable, +) from pydantic import Field, computed_field +from backend.data.event_bus import AsyncRedisEventBus from backend.data.includes import INTEGRATION_WEBHOOK_INCLUDE -from backend.data.queue import AsyncRedisEventBus from backend.integrations.providers import ProviderName from backend.integrations.webhooks.utils import webhook_ingress_url +from backend.server.v2.library.model import LibraryAgentPreset from backend.util.exceptions import NotFoundError from .db import BaseDbModel - -if TYPE_CHECKING: - from .graph import NodeModel +from .graph import NodeModel logger = logging.getLogger(__name__) @@ -32,8 +36,6 @@ class Webhook(BaseDbModel): provider_webhook_id: str - attached_nodes: Optional[list["NodeModel"]] = None - @computed_field @property def url(self) -> str: @@ -41,8 +43,6 @@ class Webhook(BaseDbModel): @staticmethod def from_db(webhook: IntegrationWebhook): - from .graph import NodeModel - return Webhook( id=webhook.id, user_id=webhook.userId, @@ -54,11 +54,26 @@ class Webhook(BaseDbModel): config=dict(webhook.config), secret=webhook.secret, provider_webhook_id=webhook.providerWebhookId, - attached_nodes=( - [NodeModel.from_db(node) for node in webhook.AgentNodes] - if webhook.AgentNodes is not None - else None - ), + ) + + +class WebhookWithRelations(Webhook): + triggered_nodes: list[NodeModel] + triggered_presets: list[LibraryAgentPreset] + + @staticmethod + def from_db(webhook: IntegrationWebhook): + if webhook.AgentNodes is None or webhook.AgentPresets is None: + raise ValueError( + "AgentNodes and AgentPresets must be included in " + "IntegrationWebhook query with relations" + ) + return WebhookWithRelations( + **Webhook.from_db(webhook).model_dump(), + triggered_nodes=[NodeModel.from_db(node) for node in webhook.AgentNodes], + triggered_presets=[ + LibraryAgentPreset.from_db(preset) for preset in webhook.AgentPresets + ], ) @@ -83,7 +98,19 @@ async def create_webhook(webhook: Webhook) -> Webhook: return Webhook.from_db(created_webhook) -async def get_webhook(webhook_id: str) -> Webhook: +@overload +async def get_webhook( + webhook_id: str, *, include_relations: Literal[True] +) -> WebhookWithRelations: ... +@overload +async def get_webhook( + webhook_id: str, *, include_relations: Literal[False] = False +) -> Webhook: ... + + +async def get_webhook( + webhook_id: str, *, include_relations: bool = False +) -> Webhook | WebhookWithRelations: """ ⚠️ No `user_id` check: DO NOT USE without check in user-facing endpoints. @@ -92,73 +119,113 @@ async def get_webhook(webhook_id: str) -> Webhook: """ webhook = await IntegrationWebhook.prisma().find_unique( where={"id": webhook_id}, - include=INTEGRATION_WEBHOOK_INCLUDE, + include=INTEGRATION_WEBHOOK_INCLUDE if include_relations else None, ) if not webhook: raise NotFoundError(f"Webhook #{webhook_id} not found") - return Webhook.from_db(webhook) + return (WebhookWithRelations if include_relations else Webhook).from_db(webhook) -async def get_all_webhooks_by_creds(credentials_id: str) -> list[Webhook]: - """⚠️ No `user_id` check: DO NOT USE without check in user-facing endpoints.""" +@overload +async def get_all_webhooks_by_creds( + user_id: str, credentials_id: str, *, include_relations: Literal[True] +) -> list[WebhookWithRelations]: ... +@overload +async def get_all_webhooks_by_creds( + user_id: str, credentials_id: str, *, include_relations: Literal[False] = False +) -> list[Webhook]: ... + + +async def get_all_webhooks_by_creds( + user_id: str, credentials_id: str, *, include_relations: bool = False +) -> list[Webhook] | list[WebhookWithRelations]: if not credentials_id: raise ValueError("credentials_id must not be empty") webhooks = await IntegrationWebhook.prisma().find_many( - where={"credentialsId": credentials_id}, - include=INTEGRATION_WEBHOOK_INCLUDE, + where={"userId": user_id, "credentialsId": credentials_id}, + include=INTEGRATION_WEBHOOK_INCLUDE if include_relations else None, ) - return [Webhook.from_db(webhook) for webhook in webhooks] + return [ + (WebhookWithRelations if include_relations else Webhook).from_db(webhook) + for webhook in webhooks + ] async def find_webhook_by_credentials_and_props( - credentials_id: str, webhook_type: str, resource: str, events: list[str] + user_id: str, + credentials_id: str, + webhook_type: str, + resource: str, + events: list[str], ) -> Webhook | None: - """⚠️ No `user_id` check: DO NOT USE without check in user-facing endpoints.""" webhook = await IntegrationWebhook.prisma().find_first( where={ + "userId": user_id, "credentialsId": credentials_id, "webhookType": webhook_type, "resource": resource, "events": {"has_every": events}, }, - include=INTEGRATION_WEBHOOK_INCLUDE, ) return Webhook.from_db(webhook) if webhook else None async def find_webhook_by_graph_and_props( - graph_id: str, provider: str, webhook_type: str, events: list[str] + user_id: str, + provider: str, + webhook_type: str, + graph_id: Optional[str] = None, + preset_id: Optional[str] = None, ) -> Webhook | None: - """⚠️ No `user_id` check: DO NOT USE without check in user-facing endpoints.""" + """Either `graph_id` or `preset_id` must be provided.""" + where_clause: IntegrationWebhookWhereInput = { + "userId": user_id, + "provider": provider, + "webhookType": webhook_type, + } + + if preset_id: + where_clause["AgentPresets"] = {"some": {"id": preset_id}} + elif graph_id: + where_clause["AgentNodes"] = {"some": {"agentGraphId": graph_id}} + else: + raise ValueError("Either graph_id or preset_id must be provided") + webhook = await IntegrationWebhook.prisma().find_first( - where={ - "provider": provider, - "webhookType": webhook_type, - "events": {"has_every": events}, - "AgentNodes": {"some": {"agentGraphId": graph_id}}, - }, - include=INTEGRATION_WEBHOOK_INCLUDE, + where=where_clause, ) return Webhook.from_db(webhook) if webhook else None -async def update_webhook_config(webhook_id: str, updated_config: dict) -> Webhook: +async def update_webhook( + webhook_id: str, + config: Optional[dict[str, Serializable]] = None, + events: Optional[list[str]] = None, +) -> Webhook: """⚠️ No `user_id` check: DO NOT USE without check in user-facing endpoints.""" + data: IntegrationWebhookUpdateInput = {} + if config is not None: + data["config"] = Json(config) + if events is not None: + data["events"] = events + if not data: + raise ValueError("Empty update query") + _updated_webhook = await IntegrationWebhook.prisma().update( where={"id": webhook_id}, - data={"config": Json(updated_config)}, - include=INTEGRATION_WEBHOOK_INCLUDE, + data=data, ) if _updated_webhook is None: - raise ValueError(f"Webhook #{webhook_id} not found") + raise NotFoundError(f"Webhook #{webhook_id} not found") return Webhook.from_db(_updated_webhook) -async def delete_webhook(webhook_id: str) -> None: - """⚠️ No `user_id` check: DO NOT USE without check in user-facing endpoints.""" - deleted = await IntegrationWebhook.prisma().delete(where={"id": webhook_id}) - if not deleted: - raise ValueError(f"Webhook #{webhook_id} not found") +async def delete_webhook(user_id: str, webhook_id: str) -> None: + deleted = await IntegrationWebhook.prisma().delete_many( + where={"id": webhook_id, "userId": user_id} + ) + if deleted < 1: + raise NotFoundError(f"Webhook #{webhook_id} not found") # --------------------- WEBHOOK EVENTS --------------------- # diff --git a/autogpt_platform/backend/backend/data/model.py b/autogpt_platform/backend/backend/data/model.py index 9592e6abc6..e1f8743ad4 100644 --- a/autogpt_platform/backend/backend/data/model.py +++ b/autogpt_platform/backend/backend/data/model.py @@ -14,11 +14,12 @@ from typing import ( Generic, Literal, Optional, - Sequence, TypedDict, TypeVar, + cast, get_args, ) +from urllib.parse import urlparse from uuid import uuid4 from prisma.enums import CreditTransactionType @@ -240,13 +241,65 @@ class UserPasswordCredentials(_BaseCredentials): return f"Basic {base64.b64encode(f'{self.username.get_secret_value()}:{self.password.get_secret_value()}'.encode()).decode()}" +class HostScopedCredentials(_BaseCredentials): + type: Literal["host_scoped"] = "host_scoped" + host: str = Field(description="The host/URI pattern to match against request URLs") + headers: dict[str, SecretStr] = Field( + description="Key-value header map to add to matching requests", + default_factory=dict, + ) + + def _extract_headers(self, headers: dict[str, SecretStr]) -> dict[str, str]: + """Helper to extract secret values from headers.""" + return {key: value.get_secret_value() for key, value in headers.items()} + + @field_serializer("headers") + def serialize_headers(self, headers: dict[str, SecretStr]) -> dict[str, str]: + """Serialize headers by extracting secret values.""" + return self._extract_headers(headers) + + def get_headers_dict(self) -> dict[str, str]: + """Get headers with secret values extracted.""" + return self._extract_headers(self.headers) + + def auth_header(self) -> str: + """Get authorization header for backward compatibility.""" + auth_headers = self.get_headers_dict() + if "Authorization" in auth_headers: + return auth_headers["Authorization"] + return "" + + def matches_url(self, url: str) -> bool: + """Check if this credential should be applied to the given URL.""" + + parsed_url = urlparse(url) + # Extract hostname without port + request_host = parsed_url.hostname + if not request_host: + return False + + # Simple host matching - exact match or wildcard subdomain match + if self.host == request_host: + return True + + # Support wildcard matching (e.g., "*.example.com" matches "api.example.com") + if self.host.startswith("*."): + domain = self.host[2:] # Remove "*." + return request_host.endswith(f".{domain}") or request_host == domain + + return False + + Credentials = Annotated[ - OAuth2Credentials | APIKeyCredentials | UserPasswordCredentials, + OAuth2Credentials + | APIKeyCredentials + | UserPasswordCredentials + | HostScopedCredentials, Field(discriminator="type"), ] -CredentialsType = Literal["api_key", "oauth2", "user_password"] +CredentialsType = Literal["api_key", "oauth2", "user_password", "host_scoped"] class OAuthState(BaseModel): @@ -320,15 +373,29 @@ class CredentialsMetaInput(BaseModel, Generic[CP, CT]): ) @staticmethod - def _add_json_schema_extra(schema, cls: CredentialsMetaInput): - schema["credentials_provider"] = cls.allowed_providers() - schema["credentials_types"] = cls.allowed_cred_types() + def _add_json_schema_extra(schema: dict, model_class: type): + # Use model_class for allowed_providers/cred_types + if hasattr(model_class, "allowed_providers") and hasattr( + model_class, "allowed_cred_types" + ): + schema["credentials_provider"] = model_class.allowed_providers() + schema["credentials_types"] = model_class.allowed_cred_types() + # Do not return anything, just mutate schema in place model_config = ConfigDict( json_schema_extra=_add_json_schema_extra, # type: ignore ) +def _extract_host_from_url(url: str) -> str: + """Extract host from URL for grouping host-scoped credentials.""" + try: + parsed = urlparse(url) + return parsed.hostname or url + except Exception: + return "" + + class CredentialsFieldInfo(BaseModel, Generic[CP, CT]): # TODO: move discrimination mechanism out of CredentialsField (frontend + backend) provider: frozenset[CP] = Field(..., alias="credentials_provider") @@ -336,11 +403,12 @@ class CredentialsFieldInfo(BaseModel, Generic[CP, CT]): required_scopes: Optional[frozenset[str]] = Field(None, alias="credentials_scopes") discriminator: Optional[str] = None discriminator_mapping: Optional[dict[str, CP]] = None + discriminator_values: set[Any] = Field(default_factory=set) @classmethod def combine( cls, *fields: tuple[CredentialsFieldInfo[CP, CT], T] - ) -> Sequence[tuple[CredentialsFieldInfo[CP, CT], set[T]]]: + ) -> dict[str, tuple[CredentialsFieldInfo[CP, CT], set[T]]]: """ Combines multiple CredentialsFieldInfo objects into as few as possible. @@ -358,22 +426,36 @@ class CredentialsFieldInfo(BaseModel, Generic[CP, CT]): the set of keys of the respective original items that were grouped together. """ if not fields: - return [] + return {} # Group fields by their provider and supported_types + # For HTTP host-scoped credentials, also group by host grouped_fields: defaultdict[ tuple[frozenset[CP], frozenset[CT]], list[tuple[T, CredentialsFieldInfo[CP, CT]]], ] = defaultdict(list) for field, key in fields: - group_key = (frozenset(field.provider), frozenset(field.supported_types)) + if field.provider == frozenset([ProviderName.HTTP]): + # HTTP host-scoped credentials can have different hosts that reqires different credential sets. + # Group by host extracted from the URL + providers = frozenset( + [cast(CP, "http")] + + [ + cast(CP, _extract_host_from_url(str(value))) + for value in field.discriminator_values + ] + ) + else: + providers = frozenset(field.provider) + + group_key = (providers, frozenset(field.supported_types)) grouped_fields[group_key].append((key, field)) # Combine fields within each group - result: list[tuple[CredentialsFieldInfo[CP, CT], set[T]]] = [] + result: dict[str, tuple[CredentialsFieldInfo[CP, CT], set[T]]] = {} - for group in grouped_fields.values(): + for key, group in grouped_fields.items(): # Start with the first field in the group _, combined = group[0] @@ -386,18 +468,32 @@ class CredentialsFieldInfo(BaseModel, Generic[CP, CT]): if field.required_scopes: all_scopes.update(field.required_scopes) - # Create a new combined field - result.append( - ( - CredentialsFieldInfo[CP, CT]( - credentials_provider=combined.provider, - credentials_types=combined.supported_types, - credentials_scopes=frozenset(all_scopes) or None, - discriminator=combined.discriminator, - discriminator_mapping=combined.discriminator_mapping, - ), - combined_keys, - ) + # Combine discriminator_values from all fields in the group (removing duplicates) + all_discriminator_values = [] + for _, field in group: + for value in field.discriminator_values: + if value not in all_discriminator_values: + all_discriminator_values.append(value) + + # Generate the key for the combined result + providers_key, supported_types_key = key + group_key = ( + "-".join(sorted(providers_key)) + + "_" + + "-".join(sorted(supported_types_key)) + + "_credentials" + ) + + result[group_key] = ( + CredentialsFieldInfo[CP, CT]( + credentials_provider=combined.provider, + credentials_types=combined.supported_types, + credentials_scopes=frozenset(all_scopes) or None, + discriminator=combined.discriminator, + discriminator_mapping=combined.discriminator_mapping, + discriminator_values=set(all_discriminator_values), + ), + combined_keys, ) return result @@ -406,11 +502,15 @@ class CredentialsFieldInfo(BaseModel, Generic[CP, CT]): if not (self.discriminator and self.discriminator_mapping): return self - discriminator_value = self.discriminator_mapping[discriminator_value] return CredentialsFieldInfo( - credentials_provider=frozenset([discriminator_value]), + credentials_provider=frozenset( + [self.discriminator_mapping[discriminator_value]] + ), credentials_types=self.supported_types, credentials_scopes=self.required_scopes, + discriminator=self.discriminator, + discriminator_mapping=self.discriminator_mapping, + discriminator_values=self.discriminator_values, ) @@ -419,6 +519,7 @@ def CredentialsField( *, discriminator: Optional[str] = None, discriminator_mapping: Optional[dict[str, Any]] = None, + discriminator_values: Optional[set[Any]] = None, title: Optional[str] = None, description: Optional[str] = None, **kwargs, @@ -434,6 +535,7 @@ def CredentialsField( "credentials_scopes": list(required_scopes) or None, "discriminator": discriminator, "discriminator_mapping": discriminator_mapping, + "discriminator_values": discriminator_values, }.items() if v is not None } diff --git a/autogpt_platform/backend/backend/data/model_test.py b/autogpt_platform/backend/backend/data/model_test.py new file mode 100644 index 0000000000..37ec6be82f --- /dev/null +++ b/autogpt_platform/backend/backend/data/model_test.py @@ -0,0 +1,143 @@ +import pytest +from pydantic import SecretStr + +from backend.data.model import HostScopedCredentials + + +class TestHostScopedCredentials: + def test_host_scoped_credentials_creation(self): + """Test creating HostScopedCredentials with required fields.""" + creds = HostScopedCredentials( + provider="custom", + host="api.example.com", + headers={ + "Authorization": SecretStr("Bearer secret-token"), + "X-API-Key": SecretStr("api-key-123"), + }, + title="Example API Credentials", + ) + + assert creds.type == "host_scoped" + assert creds.provider == "custom" + assert creds.host == "api.example.com" + assert creds.title == "Example API Credentials" + assert len(creds.headers) == 2 + assert "Authorization" in creds.headers + assert "X-API-Key" in creds.headers + + def test_get_headers_dict(self): + """Test getting headers with secret values extracted.""" + creds = HostScopedCredentials( + provider="custom", + host="api.example.com", + headers={ + "Authorization": SecretStr("Bearer secret-token"), + "X-Custom-Header": SecretStr("custom-value"), + }, + ) + + headers_dict = creds.get_headers_dict() + + assert headers_dict == { + "Authorization": "Bearer secret-token", + "X-Custom-Header": "custom-value", + } + + def test_matches_url_exact_host(self): + """Test URL matching with exact host match.""" + creds = HostScopedCredentials( + provider="custom", + host="api.example.com", + headers={"Authorization": SecretStr("Bearer token")}, + ) + + assert creds.matches_url("https://api.example.com/v1/data") + assert creds.matches_url("http://api.example.com/endpoint") + assert not creds.matches_url("https://other.example.com/v1/data") + assert not creds.matches_url("https://subdomain.api.example.com/v1/data") + + def test_matches_url_wildcard_subdomain(self): + """Test URL matching with wildcard subdomain pattern.""" + creds = HostScopedCredentials( + provider="custom", + host="*.example.com", + headers={"Authorization": SecretStr("Bearer token")}, + ) + + assert creds.matches_url("https://api.example.com/v1/data") + assert creds.matches_url("https://subdomain.example.com/endpoint") + assert creds.matches_url("https://deep.nested.example.com/path") + assert creds.matches_url("https://example.com/path") # Base domain should match + assert not creds.matches_url("https://example.org/v1/data") + assert not creds.matches_url("https://notexample.com/v1/data") + + def test_matches_url_with_port_and_path(self): + """Test URL matching with ports and paths.""" + creds = HostScopedCredentials( + provider="custom", + host="localhost", + headers={"Authorization": SecretStr("Bearer token")}, + ) + + assert creds.matches_url("http://localhost:8080/api/v1") + assert creds.matches_url("https://localhost:443/secure/endpoint") + assert creds.matches_url("http://localhost/simple") + + def test_empty_headers_dict(self): + """Test HostScopedCredentials with empty headers.""" + creds = HostScopedCredentials( + provider="custom", host="api.example.com", headers={} + ) + + assert creds.get_headers_dict() == {} + assert creds.matches_url("https://api.example.com/test") + + def test_credential_serialization(self): + """Test that credentials can be serialized/deserialized properly.""" + original_creds = HostScopedCredentials( + provider="custom", + host="api.example.com", + headers={ + "Authorization": SecretStr("Bearer secret-token"), + "X-API-Key": SecretStr("api-key-123"), + }, + title="Test Credentials", + ) + + # Serialize to dict (simulating storage) + serialized = original_creds.model_dump() + + # Deserialize back + restored_creds = HostScopedCredentials.model_validate(serialized) + + assert restored_creds.id == original_creds.id + assert restored_creds.provider == original_creds.provider + assert restored_creds.host == original_creds.host + assert restored_creds.title == original_creds.title + assert restored_creds.type == "host_scoped" + + # Check that headers are properly restored + assert restored_creds.get_headers_dict() == original_creds.get_headers_dict() + + @pytest.mark.parametrize( + "host,test_url,expected", + [ + ("api.example.com", "https://api.example.com/test", True), + ("api.example.com", "https://different.example.com/test", False), + ("*.example.com", "https://api.example.com/test", True), + ("*.example.com", "https://sub.api.example.com/test", True), + ("*.example.com", "https://example.com/test", True), + ("*.example.com", "https://example.org/test", False), + ("localhost", "http://localhost:3000/test", True), + ("localhost", "http://127.0.0.1:3000/test", False), + ], + ) + def test_url_matching_parametrized(self, host: str, test_url: str, expected: bool): + """Parametrized test for various URL matching scenarios.""" + creds = HostScopedCredentials( + provider="test", + host=host, + headers={"Authorization": SecretStr("Bearer token")}, + ) + + assert creds.matches_url(test_url) == expected diff --git a/autogpt_platform/backend/backend/data/redis.py b/autogpt_platform/backend/backend/data/redis_client.py similarity index 100% rename from autogpt_platform/backend/backend/data/redis.py rename to autogpt_platform/backend/backend/data/redis_client.py diff --git a/autogpt_platform/backend/backend/executor/manager.py b/autogpt_platform/backend/backend/executor/manager.py index 1736539230..db0d2db37f 100644 --- a/autogpt_platform/backend/backend/executor/manager.py +++ b/autogpt_platform/backend/backend/executor/manager.py @@ -12,14 +12,11 @@ from typing import TYPE_CHECKING, Any, Optional, TypeVar, cast from pika.adapters.blocking_connection import BlockingChannel from pika.spec import Basic, BasicProperties +from pydantic import JsonValue from redis.asyncio.lock import Lock as RedisLock from backend.blocks.io import AgentOutputBlock -from backend.data.model import ( - CredentialsMetaInput, - GraphExecutionStats, - NodeExecutionStats, -) +from backend.data.model import GraphExecutionStats, NodeExecutionStats from backend.data.notifications import ( AgentRunData, LowBalanceData, @@ -38,7 +35,7 @@ from autogpt_libs.utils.cache import thread_cached from prometheus_client import Gauge, start_http_server from backend.blocks.agent import AgentExecutorBlock -from backend.data import redis +from backend.data import redis_client as redis from backend.data.block import ( BlockData, BlockInput, @@ -138,9 +135,7 @@ async def execute_node( creds_manager: IntegrationCredentialsManager, data: NodeExecutionEntry, execution_stats: NodeExecutionStats | None = None, - node_credentials_input_map: Optional[ - dict[str, dict[str, CredentialsMetaInput]] - ] = None, + nodes_input_masks: Optional[dict[str, dict[str, JsonValue]]] = None, ) -> BlockOutput: """ Execute a node in the graph. This will trigger a block execution on a node, @@ -183,8 +178,8 @@ async def execute_node( if isinstance(node_block, AgentExecutorBlock): _input_data = AgentExecutorBlock.Input(**node.input_default) _input_data.inputs = input_data - if node_credentials_input_map: - _input_data.node_credentials_input_map = node_credentials_input_map + if nodes_input_masks: + _input_data.nodes_input_masks = nodes_input_masks input_data = _input_data.model_dump() data.inputs = input_data @@ -255,7 +250,7 @@ async def _enqueue_next_nodes( graph_exec_id: str, graph_id: str, log_metadata: LogMetadata, - node_credentials_input_map: Optional[dict[str, dict[str, CredentialsMetaInput]]], + nodes_input_masks: Optional[dict[str, dict[str, JsonValue]]], ) -> list[NodeExecutionEntry]: async def add_enqueued_execution( node_exec_id: str, node_id: str, block_id: str, data: BlockInput @@ -326,14 +321,12 @@ async def _enqueue_next_nodes( for name in static_link_names: next_node_input[name] = latest_execution.input_data.get(name) - # Apply node credentials overrides - node_credentials = None - if node_credentials_input_map and ( - node_credentials := node_credentials_input_map.get(next_node.id) + # Apply node input overrides + node_input_mask = None + if nodes_input_masks and ( + node_input_mask := nodes_input_masks.get(next_node.id) ): - next_node_input.update( - {k: v.model_dump() for k, v in node_credentials.items()} - ) + next_node_input.update(node_input_mask) # Validate the input data for the next node. next_node_input, validation_msg = validate_exec(next_node, next_node_input) @@ -377,11 +370,9 @@ async def _enqueue_next_nodes( for input_name in static_link_names: idata[input_name] = next_node_input[input_name] - # Apply node credentials overrides - if node_credentials: - idata.update( - {k: v.model_dump() for k, v in node_credentials.items()} - ) + # Apply node input overrides + if node_input_mask: + idata.update(node_input_mask) idata, msg = validate_exec(next_node, idata) suffix = f"{next_output_name}>{next_input_name}~{ineid}:{msg}" @@ -430,14 +421,12 @@ class Executor: """ @classmethod - @async_error_logged + @async_error_logged(swallow=True) async def on_node_execution( cls, node_exec: NodeExecutionEntry, node_exec_progress: NodeExecutionProgress, - node_credentials_input_map: Optional[ - dict[str, dict[str, CredentialsMetaInput]] - ] = None, + nodes_input_masks: Optional[dict[str, dict[str, JsonValue]]] = None, ) -> NodeExecutionStats: log_metadata = LogMetadata( user_id=node_exec.user_id, @@ -458,7 +447,7 @@ class Executor: db_client=db_client, log_metadata=log_metadata, stats=execution_stats, - node_credentials_input_map=node_credentials_input_map, + nodes_input_masks=nodes_input_masks, ) execution_stats.walltime = timing_info.wall_time execution_stats.cputime = timing_info.cpu_time @@ -481,9 +470,7 @@ class Executor: db_client: "DatabaseManagerAsyncClient", log_metadata: LogMetadata, stats: NodeExecutionStats | None = None, - node_credentials_input_map: Optional[ - dict[str, dict[str, CredentialsMetaInput]] - ] = None, + nodes_input_masks: Optional[dict[str, dict[str, JsonValue]]] = None, ): try: log_metadata.info(f"Start node execution {node_exec.node_exec_id}") @@ -498,7 +485,7 @@ class Executor: creds_manager=cls.creds_manager, data=node_exec, execution_stats=stats, - node_credentials_input_map=node_credentials_input_map, + nodes_input_masks=nodes_input_masks, ): node_exec_progress.add_output( ExecutionOutputEntry( @@ -542,7 +529,7 @@ class Executor: logger.info(f"[GraphExecutor] {cls.pid} started") @classmethod - @error_logged + @error_logged(swallow=False) def on_graph_execution( cls, graph_exec: GraphExecutionEntry, cancel: threading.Event ): @@ -594,6 +581,15 @@ class Executor: exec_stats.cputime += timing_info.cpu_time exec_stats.error = str(error) if error else exec_stats.error + if status not in { + ExecutionStatus.COMPLETED, + ExecutionStatus.TERMINATED, + ExecutionStatus.FAILED, + }: + raise RuntimeError( + f"Graph Execution #{graph_exec.graph_exec_id} ended with unexpected status {status}" + ) + if graph_exec_result := db_client.update_graph_execution_stats( graph_exec_id=graph_exec.graph_exec_id, status=status, @@ -697,7 +693,6 @@ class Executor: if _graph_exec := db_client.update_graph_execution_stats( graph_exec_id=graph_exec.graph_exec_id, - status=execution_status, stats=execution_stats, ): send_execution_update(_graph_exec) @@ -779,24 +774,19 @@ class Executor: ) raise - # Add credential overrides ----------------------------- + # Add input overrides ----------------------------- node_id = queued_node_exec.node_id - if (node_creds_map := graph_exec.node_credentials_input_map) and ( - node_field_creds_map := node_creds_map.get(node_id) + if (nodes_input_masks := graph_exec.nodes_input_masks) and ( + node_input_mask := nodes_input_masks.get(node_id) ): - queued_node_exec.inputs.update( - { - field_name: creds_meta.model_dump() - for field_name, creds_meta in node_field_creds_map.items() - } - ) + queued_node_exec.inputs.update(node_input_mask) # Kick off async node execution ------------------------- node_execution_task = asyncio.run_coroutine_threadsafe( cls.on_node_execution( node_exec=queued_node_exec, node_exec_progress=running_node_execution[node_id], - node_credentials_input_map=node_creds_map, + nodes_input_masks=nodes_input_masks, ), cls.node_execution_loop, ) @@ -840,7 +830,7 @@ class Executor: node_id=node_id, graph_exec=graph_exec, log_metadata=log_metadata, - node_creds_map=node_creds_map, + nodes_input_masks=nodes_input_masks, execution_queue=execution_queue, ), cls.node_evaluation_loop, @@ -910,7 +900,7 @@ class Executor: node_id: str, graph_exec: GraphExecutionEntry, log_metadata: LogMetadata, - node_creds_map: Optional[dict[str, dict[str, CredentialsMetaInput]]], + nodes_input_masks: Optional[dict[str, dict[str, JsonValue]]], execution_queue: ExecutionQueue[NodeExecutionEntry], ) -> None: """Process a node's output, update its status, and enqueue next nodes. @@ -920,7 +910,7 @@ class Executor: node_id: The ID of the node that produced the output graph_exec: The graph execution entry log_metadata: Logger metadata for consistent logging - node_creds_map: Optional map of node credentials + nodes_input_masks: Optional map of node input overrides execution_queue: Queue to add next executions to """ db_client = get_db_async_client() @@ -944,7 +934,7 @@ class Executor: graph_exec_id=graph_exec.graph_exec_id, graph_id=graph_exec.graph_id, log_metadata=log_metadata, - node_credentials_input_map=node_creds_map, + nodes_input_masks=nodes_input_masks, ): execution_queue.add(next_execution) except Exception as e: diff --git a/autogpt_platform/backend/test/executor/test_manager.py b/autogpt_platform/backend/backend/executor/manager_test.py similarity index 98% rename from autogpt_platform/backend/test/executor/test_manager.py rename to autogpt_platform/backend/backend/executor/manager_test.py index c07ac73b94..96eeb28f98 100644 --- a/autogpt_platform/backend/test/executor/test_manager.py +++ b/autogpt_platform/backend/backend/executor/manager_test.py @@ -366,14 +366,13 @@ async def test_execute_preset(server: SpinTestServer): "dictionary": {"key1": "Hello", "key2": "World"}, "selected_value": "key2", }, + credentials={}, is_active=True, ) created_preset = await server.agent_server.test_create_preset(preset, test_user.id) # Execute preset with overriding values result = await server.agent_server.test_execute_preset( - graph_id=test_graph.id, - graph_version=test_graph.version, preset_id=created_preset.id, user_id=test_user.id, ) @@ -455,16 +454,15 @@ async def test_execute_preset_with_clash(server: SpinTestServer): "dictionary": {"key1": "Hello", "key2": "World"}, "selected_value": "key2", }, + credentials={}, is_active=True, ) created_preset = await server.agent_server.test_create_preset(preset, test_user.id) # Execute preset with overriding values result = await server.agent_server.test_execute_preset( - graph_id=test_graph.id, - graph_version=test_graph.version, preset_id=created_preset.id, - node_input={"selected_value": "key1"}, + inputs={"selected_value": "key1"}, user_id=test_user.id, ) diff --git a/autogpt_platform/backend/backend/executor/scheduler.py b/autogpt_platform/backend/backend/executor/scheduler.py index 0c25e58f94..e642dbd027 100644 --- a/autogpt_platform/backend/backend/executor/scheduler.py +++ b/autogpt_platform/backend/backend/executor/scheduler.py @@ -3,6 +3,7 @@ import logging import os from datetime import datetime, timedelta, timezone from enum import Enum +from typing import Optional from urllib.parse import parse_qs, urlencode, urlparse, urlunparse from apscheduler.events import EVENT_JOB_ERROR, EVENT_JOB_EXECUTED @@ -14,13 +15,16 @@ from apscheduler.triggers.cron import CronTrigger from autogpt_libs.utils.cache import thread_cached from dotenv import load_dotenv from prisma.enums import NotificationType -from pydantic import BaseModel, ValidationError +from pydantic import BaseModel, Field, ValidationError from sqlalchemy import MetaData, create_engine from backend.data.block import BlockInput from backend.data.execution import ExecutionStatus +from backend.data.model import CredentialsMetaInput from backend.executor import utils as execution_utils from backend.notifications.notifications import NotificationManagerClient +from backend.util.exceptions import NotAuthorizedError, NotFoundError +from backend.util.logging import PrefixFilter from backend.util.metrics import sentry_capture_error from backend.util.service import ( AppService, @@ -52,19 +56,19 @@ def _extract_schema_from_url(database_url) -> tuple[str, str]: logger = logging.getLogger(__name__) +logger.addFilter(PrefixFilter("[Scheduler]")) +apscheduler_logger = logger.getChild("apscheduler") +apscheduler_logger.addFilter(PrefixFilter("[Scheduler] [APScheduler]")) + config = Config() -def log(msg, **kwargs): - logger.info("[Scheduler] " + msg, **kwargs) - - def job_listener(event): """Logs job execution outcomes for better monitoring.""" if event.exception: - log(f"Job {event.job_id} failed.") + logger.error(f"Job {event.job_id} failed.") else: - log(f"Job {event.job_id} completed successfully.") + logger.info(f"Job {event.job_id} completed successfully.") @thread_cached @@ -84,16 +88,17 @@ def execute_graph(**kwargs): async def _execute_graph(**kwargs): args = GraphExecutionJobArgs(**kwargs) try: - log(f"Executing recurring job for graph #{args.graph_id}") + logger.info(f"Executing recurring job for graph #{args.graph_id}") await execution_utils.add_graph_execution( - graph_id=args.graph_id, - inputs=args.input_data, user_id=args.user_id, + graph_id=args.graph_id, graph_version=args.graph_version, + inputs=args.input_data, + graph_credentials_inputs=args.input_credentials, use_db_query=False, ) except Exception as e: - logger.exception(f"Error executing graph {args.graph_id}: {e}") + logger.error(f"Error executing graph {args.graph_id}: {e}") class LateExecutionException(Exception): @@ -137,20 +142,20 @@ def report_late_executions() -> str: def process_existing_batches(**kwargs): args = NotificationJobArgs(**kwargs) try: - log( + logger.info( f"Processing existing batches for notification type {args.notification_types}" ) get_notification_client().process_existing_batches(args.notification_types) except Exception as e: - logger.exception(f"Error processing existing batches: {e}") + logger.error(f"Error processing existing batches: {e}") def process_weekly_summary(**kwargs): try: - log("Processing weekly summary") + logger.info("Processing weekly summary") get_notification_client().queue_weekly_summary() except Exception as e: - logger.exception(f"Error processing weekly summary: {e}") + logger.error(f"Error processing weekly summary: {e}") class Jobstores(Enum): @@ -160,11 +165,12 @@ class Jobstores(Enum): class GraphExecutionJobArgs(BaseModel): - graph_id: str - input_data: BlockInput user_id: str + graph_id: str graph_version: int cron: str + input_data: BlockInput + input_credentials: dict[str, CredentialsMetaInput] = Field(default_factory=dict) class GraphExecutionJobInfo(GraphExecutionJobArgs): @@ -247,7 +253,8 @@ class Scheduler(AppService): ), # These don't really need persistence Jobstores.WEEKLY_NOTIFICATIONS.value: MemoryJobStore(), - } + }, + logger=apscheduler_logger, ) if self.register_system_tasks: @@ -285,34 +292,40 @@ class Scheduler(AppService): def cleanup(self): super().cleanup() - logger.info(f"[{self.service_name}] ⏳ Shutting down scheduler...") + logger.info("⏳ Shutting down scheduler...") if self.scheduler: self.scheduler.shutdown(wait=False) @expose def add_graph_execution_schedule( self, + user_id: str, graph_id: str, graph_version: int, cron: str, input_data: BlockInput, - user_id: str, + input_credentials: dict[str, CredentialsMetaInput], + name: Optional[str] = None, ) -> GraphExecutionJobInfo: job_args = GraphExecutionJobArgs( - graph_id=graph_id, - input_data=input_data, user_id=user_id, + graph_id=graph_id, graph_version=graph_version, cron=cron, + input_data=input_data, + input_credentials=input_credentials, ) job = self.scheduler.add_job( execute_graph, - CronTrigger.from_crontab(cron), kwargs=job_args.model_dump(), - replace_existing=True, + name=name, + trigger=CronTrigger.from_crontab(cron), jobstore=Jobstores.EXECUTION.value, + replace_existing=True, + ) + logger.info( + f"Added job {job.id} with cron schedule '{cron}' input data: {input_data}" ) - log(f"Added job {job.id} with cron schedule '{cron}' input data: {input_data}") return GraphExecutionJobInfo.from_db(job_args, job) @expose @@ -321,14 +334,13 @@ class Scheduler(AppService): ) -> GraphExecutionJobInfo: job = self.scheduler.get_job(schedule_id, jobstore=Jobstores.EXECUTION.value) if not job: - log(f"Job {schedule_id} not found.") - raise ValueError(f"Job #{schedule_id} not found.") + raise NotFoundError(f"Job #{schedule_id} not found.") job_args = GraphExecutionJobArgs(**job.kwargs) if job_args.user_id != user_id: - raise ValueError("User ID does not match the job's user ID.") + raise NotAuthorizedError("User ID does not match the job's user ID") - log(f"Deleting job {schedule_id}") + logger.info(f"Deleting job {schedule_id}") job.remove() return GraphExecutionJobInfo.from_db(job_args, job) diff --git a/autogpt_platform/backend/test/executor/test_scheduler.py b/autogpt_platform/backend/backend/executor/scheduler_test.py similarity index 97% rename from autogpt_platform/backend/test/executor/test_scheduler.py rename to autogpt_platform/backend/backend/executor/scheduler_test.py index 73e6a2e82a..f6b2b028c2 100644 --- a/autogpt_platform/backend/test/executor/test_scheduler.py +++ b/autogpt_platform/backend/backend/executor/scheduler_test.py @@ -27,6 +27,7 @@ async def test_agent_schedule(server: SpinTestServer): graph_version=1, cron="0 0 * * *", input_data={"input": "data"}, + input_credentials={}, ) assert schedule diff --git a/autogpt_platform/backend/test/executor/test_execution_functions.py b/autogpt_platform/backend/backend/executor/util_test.py similarity index 100% rename from autogpt_platform/backend/test/executor/test_execution_functions.py rename to autogpt_platform/backend/backend/executor/util_test.py diff --git a/autogpt_platform/backend/backend/executor/utils.py b/autogpt_platform/backend/backend/executor/utils.py index 4ef18d509f..329d6fab2d 100644 --- a/autogpt_platform/backend/backend/executor/utils.py +++ b/autogpt_platform/backend/backend/executor/utils.py @@ -5,7 +5,7 @@ from concurrent.futures import Future from typing import TYPE_CHECKING, Any, Callable, Optional, cast from autogpt_libs.utils.cache import thread_cached -from pydantic import BaseModel +from pydantic import BaseModel, JsonValue from backend.data.block import ( Block, @@ -435,9 +435,7 @@ def validate_exec( async def _validate_node_input_credentials( graph: GraphModel, user_id: str, - node_credentials_input_map: Optional[ - dict[str, dict[str, CredentialsMetaInput]] - ] = None, + nodes_input_masks: Optional[dict[str, dict[str, JsonValue]]] = None, ): """Checks all credentials for all nodes of the graph""" @@ -453,11 +451,13 @@ async def _validate_node_input_credentials( for field_name, credentials_meta_type in credentials_fields.items(): if ( - node_credentials_input_map - and (node_credentials_inputs := node_credentials_input_map.get(node.id)) - and field_name in node_credentials_inputs + nodes_input_masks + and (node_input_mask := nodes_input_masks.get(node.id)) + and field_name in node_input_mask ): - credentials_meta = node_credentials_input_map[node.id][field_name] + credentials_meta = credentials_meta_type.model_validate( + node_input_mask[field_name] + ) elif field_name in node.input_default: credentials_meta = credentials_meta_type.model_validate( node.input_default[field_name] @@ -496,7 +496,7 @@ async def _validate_node_input_credentials( def make_node_credentials_input_map( graph: GraphModel, graph_credentials_input: dict[str, CredentialsMetaInput], -) -> dict[str, dict[str, CredentialsMetaInput]]: +) -> dict[str, dict[str, JsonValue]]: """ Maps credentials for an execution to the correct nodes. @@ -505,9 +505,9 @@ def make_node_credentials_input_map( graph_credentials_input: A (graph_input_name, credentials_meta) map. Returns: - dict[node_id, dict[field_name, CredentialsMetaInput]]: Node credentials input map. + dict[node_id, dict[field_name, CredentialsMetaRaw]]: Node credentials input map. """ - result: dict[str, dict[str, CredentialsMetaInput]] = {} + result: dict[str, dict[str, JsonValue]] = {} # Get aggregated credentials fields for the graph graph_cred_inputs = graph.aggregate_credentials_inputs() @@ -521,7 +521,9 @@ def make_node_credentials_input_map( for node_id, node_field_name in compatible_node_fields: if node_id not in result: result[node_id] = {} - result[node_id][node_field_name] = graph_credentials_input[graph_input_name] + result[node_id][node_field_name] = graph_credentials_input[ + graph_input_name + ].model_dump(exclude_none=True) return result @@ -530,9 +532,7 @@ async def construct_node_execution_input( graph: GraphModel, user_id: str, graph_inputs: BlockInput, - node_credentials_input_map: Optional[ - dict[str, dict[str, CredentialsMetaInput]] - ] = None, + nodes_input_masks: Optional[dict[str, dict[str, JsonValue]]] = None, ) -> list[tuple[str, BlockInput]]: """ Validates and prepares the input data for executing a graph. @@ -550,8 +550,8 @@ async def construct_node_execution_input( list[tuple[str, BlockInput]]: A list of tuples, each containing the node ID and the corresponding input data for that node. """ - graph.validate_graph(for_run=True) - await _validate_node_input_credentials(graph, user_id, node_credentials_input_map) + graph.validate_graph(for_run=True, nodes_input_masks=nodes_input_masks) + await _validate_node_input_credentials(graph, user_id, nodes_input_masks) nodes_input = [] for node in graph.starting_nodes: @@ -568,23 +568,9 @@ async def construct_node_execution_input( if input_name and input_name in graph_inputs: input_data = {"value": graph_inputs[input_name]} - # Extract webhook payload, and assign it to the input pin - webhook_payload_key = f"webhook_{node.webhook_id}_payload" - if ( - block.block_type in (BlockType.WEBHOOK, BlockType.WEBHOOK_MANUAL) - and node.webhook_id - ): - if webhook_payload_key not in graph_inputs: - raise ValueError( - f"Node {block.name} #{node.id} webhook payload is missing" - ) - input_data = {"payload": graph_inputs[webhook_payload_key]} - - # Apply node credentials overrides - if node_credentials_input_map and ( - node_credentials := node_credentials_input_map.get(node.id) - ): - input_data.update({k: v.model_dump() for k, v in node_credentials.items()}) + # Apply node input overrides + if nodes_input_masks and (node_input_mask := nodes_input_masks.get(node.id)): + input_data.update(node_input_mask) input_data, error = validate_exec(node, input_data) if input_data is None: @@ -600,6 +586,20 @@ async def construct_node_execution_input( return nodes_input +def _merge_nodes_input_masks( + overrides_map_1: dict[str, dict[str, JsonValue]], + overrides_map_2: dict[str, dict[str, JsonValue]], +) -> dict[str, dict[str, JsonValue]]: + """Perform a per-node merge of input overrides""" + result = overrides_map_1.copy() + for node_id, overrides2 in overrides_map_2.items(): + if node_id in result: + result[node_id] = {**result[node_id], **overrides2} + else: + result[node_id] = overrides2 + return result + + # ============ Execution Queue Helpers ============ # @@ -730,13 +730,11 @@ async def stop_graph_execution( async def add_graph_execution( graph_id: str, user_id: str, - inputs: BlockInput, + inputs: Optional[BlockInput] = None, preset_id: Optional[str] = None, graph_version: Optional[int] = None, graph_credentials_inputs: Optional[dict[str, CredentialsMetaInput]] = None, - node_credentials_input_map: Optional[ - dict[str, dict[str, CredentialsMetaInput]] - ] = None, + nodes_input_masks: Optional[dict[str, dict[str, JsonValue]]] = None, use_db_query: bool = True, ) -> GraphExecutionWithNodes: """ @@ -750,7 +748,7 @@ async def add_graph_execution( graph_version: The version of the graph to execute. graph_credentials_inputs: Credentials inputs to use in the execution. Keys should map to the keys generated by `GraphModel.aggregate_credentials_inputs`. - node_credentials_input_map: Credentials inputs to use in the execution, mapped to specific nodes. + nodes_input_masks: Node inputs to use in the execution. Returns: GraphExecutionEntry: The entry for the graph execution. Raises: @@ -774,10 +772,19 @@ async def add_graph_execution( if not graph: raise NotFoundError(f"Graph #{graph_id} not found.") - node_credentials_input_map = node_credentials_input_map or ( - make_node_credentials_input_map(graph, graph_credentials_inputs) - if graph_credentials_inputs - else None + nodes_input_masks = _merge_nodes_input_masks( + ( + make_node_credentials_input_map(graph, graph_credentials_inputs) + if graph_credentials_inputs + else {} + ), + nodes_input_masks or {}, + ) + starting_nodes_input = await construct_node_execution_input( + graph=graph, + user_id=user_id, + graph_inputs=inputs or {}, + nodes_input_masks=nodes_input_masks, ) if use_db_query: @@ -785,12 +792,7 @@ async def add_graph_execution( user_id=user_id, graph_id=graph_id, graph_version=graph.version, - starting_nodes_input=await construct_node_execution_input( - graph=graph, - user_id=user_id, - graph_inputs=inputs, - node_credentials_input_map=node_credentials_input_map, - ), + starting_nodes_input=starting_nodes_input, preset_id=preset_id, ) else: @@ -798,20 +800,15 @@ async def add_graph_execution( user_id=user_id, graph_id=graph_id, graph_version=graph.version, - starting_nodes_input=await construct_node_execution_input( - graph=graph, - user_id=user_id, - graph_inputs=inputs, - node_credentials_input_map=node_credentials_input_map, - ), + starting_nodes_input=starting_nodes_input, preset_id=preset_id, ) try: queue = await get_async_execution_queue() graph_exec_entry = graph_exec.to_graph_execution_entry() - if node_credentials_input_map: - graph_exec_entry.node_credentials_input_map = node_credentials_input_map + if nodes_input_masks: + graph_exec_entry.nodes_input_masks = nodes_input_masks await queue.publish_message( routing_key=GRAPH_EXECUTION_ROUTING_KEY, message=graph_exec_entry.model_dump_json(), diff --git a/autogpt_platform/backend/backend/integrations/credentials_store.py b/autogpt_platform/backend/backend/integrations/credentials_store.py index a86cc28e92..8added620e 100644 --- a/autogpt_platform/backend/backend/integrations/credentials_store.py +++ b/autogpt_platform/backend/backend/integrations/credentials_store.py @@ -6,7 +6,7 @@ from typing import TYPE_CHECKING, Optional from pydantic import SecretStr -from backend.data.redis import get_redis_async +from backend.data.redis_client import get_redis_async if TYPE_CHECKING: from backend.executor.database import DatabaseManagerAsyncClient diff --git a/autogpt_platform/backend/backend/integrations/creds_manager.py b/autogpt_platform/backend/backend/integrations/creds_manager.py index bacc3a53e9..d782a31dd2 100644 --- a/autogpt_platform/backend/backend/integrations/creds_manager.py +++ b/autogpt_platform/backend/backend/integrations/creds_manager.py @@ -7,7 +7,7 @@ from autogpt_libs.utils.synchronize import AsyncRedisKeyedMutex from redis.asyncio.lock import Lock as AsyncRedisLock from backend.data.model import Credentials, OAuth2Credentials -from backend.data.redis import get_redis_async +from backend.data.redis_client import get_redis_async from backend.integrations.credentials_store import IntegrationCredentialsStore from backend.integrations.oauth import HANDLERS_BY_NAME from backend.integrations.providers import ProviderName diff --git a/autogpt_platform/backend/backend/integrations/providers.py b/autogpt_platform/backend/backend/integrations/providers.py index 30444c1ea3..47a92b5829 100644 --- a/autogpt_platform/backend/backend/integrations/providers.py +++ b/autogpt_platform/backend/backend/integrations/providers.py @@ -17,6 +17,7 @@ class ProviderName(str, Enum): GOOGLE = "google" GOOGLE_MAPS = "google_maps" GROQ = "groq" + HTTP = "http" HUBSPOT = "hubspot" IDEOGRAM = "ideogram" JINA = "jina" diff --git a/autogpt_platform/backend/backend/integrations/webhooks/__init__.py b/autogpt_platform/backend/backend/integrations/webhooks/__init__.py index b1774aa3fd..a4363f2e3e 100644 --- a/autogpt_platform/backend/backend/integrations/webhooks/__init__.py +++ b/autogpt_platform/backend/backend/integrations/webhooks/__init__.py @@ -1,23 +1,22 @@ +import functools from typing import TYPE_CHECKING if TYPE_CHECKING: from ..providers import ProviderName from ._base import BaseWebhooksManager -_WEBHOOK_MANAGERS: dict["ProviderName", type["BaseWebhooksManager"]] = {} - # --8<-- [start:load_webhook_managers] +@functools.cache def load_webhook_managers() -> dict["ProviderName", type["BaseWebhooksManager"]]: - if _WEBHOOK_MANAGERS: - return _WEBHOOK_MANAGERS + webhook_managers = {} from .compass import CompassWebhookManager from .generic import GenericWebhooksManager from .github import GithubWebhooksManager from .slant3d import Slant3DWebhooksManager - _WEBHOOK_MANAGERS.update( + webhook_managers.update( { handler.PROVIDER_NAME: handler for handler in [ @@ -28,7 +27,7 @@ def load_webhook_managers() -> dict["ProviderName", type["BaseWebhooksManager"]] ] } ) - return _WEBHOOK_MANAGERS + return webhook_managers # --8<-- [end:load_webhook_managers] diff --git a/autogpt_platform/backend/backend/integrations/webhooks/_base.py b/autogpt_platform/backend/backend/integrations/webhooks/_base.py index be3a710552..dd27a853da 100644 --- a/autogpt_platform/backend/backend/integrations/webhooks/_base.py +++ b/autogpt_platform/backend/backend/integrations/webhooks/_base.py @@ -7,13 +7,14 @@ from uuid import uuid4 from fastapi import Request from strenum import StrEnum -from backend.data import integrations +import backend.data.integrations as integrations from backend.data.model import Credentials from backend.integrations.providers import ProviderName -from backend.integrations.webhooks.utils import webhook_ingress_url from backend.util.exceptions import MissingConfigError from backend.util.settings import Config +from .utils import webhook_ingress_url + logger = logging.getLogger(__name__) app_config = Config() @@ -41,44 +42,74 @@ class BaseWebhooksManager(ABC, Generic[WT]): ) if webhook := await integrations.find_webhook_by_credentials_and_props( - credentials.id, webhook_type, resource, events + user_id=user_id, + credentials_id=credentials.id, + webhook_type=webhook_type, + resource=resource, + events=events, ): return webhook + return await self._create_webhook( - user_id, webhook_type, events, resource, credentials + user_id=user_id, + webhook_type=webhook_type, + events=events, + resource=resource, + credentials=credentials, ) async def get_manual_webhook( self, user_id: str, - graph_id: str, webhook_type: WT, events: list[str], - ): - if current_webhook := await integrations.find_webhook_by_graph_and_props( - graph_id, self.PROVIDER_NAME, webhook_type, events + graph_id: Optional[str] = None, + preset_id: Optional[str] = None, + ) -> integrations.Webhook: + """ + Tries to find an existing webhook tied to `graph_id`/`preset_id`, + or creates a new webhook if none exists. + + Existing webhooks are matched by `user_id`, `webhook_type`, + and `graph_id`/`preset_id`. + + If an existing webhook is found, we check if the events match and update them + if necessary. We do this rather than creating a new webhook + to avoid changing the webhook URL for existing manual webhooks. + """ + if (graph_id or preset_id) and ( + current_webhook := await integrations.find_webhook_by_graph_and_props( + user_id=user_id, + provider=self.PROVIDER_NAME.value, + webhook_type=webhook_type, + graph_id=graph_id, + preset_id=preset_id, + ) ): + if set(current_webhook.events) != set(events): + current_webhook = await integrations.update_webhook( + current_webhook.id, events=events + ) return current_webhook + return await self._create_webhook( - user_id, - webhook_type, - events, + user_id=user_id, + webhook_type=webhook_type, + events=events, register=False, ) async def prune_webhook_if_dangling( - self, webhook_id: str, credentials: Optional[Credentials] + self, user_id: str, webhook_id: str, credentials: Optional[Credentials] ) -> bool: - webhook = await integrations.get_webhook(webhook_id) - if webhook.attached_nodes is None: - raise ValueError("Error retrieving webhook including attached nodes") - if webhook.attached_nodes: + webhook = await integrations.get_webhook(webhook_id, include_relations=True) + if webhook.triggered_nodes or webhook.triggered_presets: # Don't prune webhook if in use return False if credentials: await self._deregister_webhook(webhook, credentials) - await integrations.delete_webhook(webhook.id) + await integrations.delete_webhook(user_id, webhook.id) return True # --8<-- [start:BaseWebhooksManager3] diff --git a/autogpt_platform/backend/backend/integrations/webhooks/graph_lifecycle_hooks.py b/autogpt_platform/backend/backend/integrations/webhooks/graph_lifecycle_hooks.py index 01e7b3a49e..b4e87d5080 100644 --- a/autogpt_platform/backend/backend/integrations/webhooks/graph_lifecycle_hooks.py +++ b/autogpt_platform/backend/backend/integrations/webhooks/graph_lifecycle_hooks.py @@ -1,10 +1,12 @@ import logging from typing import TYPE_CHECKING, Optional, cast -from backend.data.block import BlockSchema, BlockWebhookConfig +from backend.data.block import BlockSchema from backend.data.graph import set_node_webhook from backend.integrations.creds_manager import IntegrationCredentialsManager -from backend.integrations.webhooks import get_webhook_manager, supports_webhooks + +from . import get_webhook_manager, supports_webhooks +from .utils import setup_webhook_for_block if TYPE_CHECKING: from backend.data.graph import GraphModel, NodeModel @@ -81,7 +83,9 @@ async def on_graph_deactivate(graph: "GraphModel", user_id: str): f"credentials #{creds_meta['id']}" ) - updated_node = await on_node_deactivate(node, credentials=node_credentials) + updated_node = await on_node_deactivate( + user_id, node, credentials=node_credentials + ) updated_nodes.append(updated_node) graph.nodes = updated_nodes @@ -96,105 +100,25 @@ async def on_node_activate( ) -> "NodeModel": """Hook to be called when the node is activated/created""" - block = node.block - - if not block.webhook_config: - return node - - provider = block.webhook_config.provider - if not supports_webhooks(provider): - raise ValueError( - f"Block #{block.id} has webhook_config for provider {provider} " - "which does not support webhooks" + if node.block.webhook_config: + new_webhook, feedback = await setup_webhook_for_block( + user_id=user_id, + trigger_block=node.block, + trigger_config=node.input_default, + for_graph_id=node.graph_id, ) - - logger.debug( - f"Activating webhook node #{node.id} with config {block.webhook_config}" - ) - - webhooks_manager = get_webhook_manager(provider) - - if auto_setup_webhook := isinstance(block.webhook_config, BlockWebhookConfig): - try: - resource = block.webhook_config.resource_format.format(**node.input_default) - except KeyError: - resource = None - logger.debug( - f"Constructed resource string {resource} from input {node.input_default}" - ) - else: - resource = "" # not relevant for manual webhooks - - block_input_schema = cast(BlockSchema, block.input_schema) - credentials_field_name = next(iter(block_input_schema.get_credentials_fields()), "") - credentials_meta = ( - node.input_default.get(credentials_field_name) - if credentials_field_name - else None - ) - event_filter_input_name = block.webhook_config.event_filter_input - has_everything_for_webhook = ( - resource is not None - and (credentials_meta or not credentials_field_name) - and ( - not event_filter_input_name - or ( - event_filter_input_name in node.input_default - and any( - is_on - for is_on in node.input_default[event_filter_input_name].values() - ) - ) - ) - ) - - if has_everything_for_webhook and resource is not None: - logger.debug(f"Node #{node} has everything for a webhook!") - if credentials_meta and not credentials: - raise ValueError( - f"Cannot set up webhook for node #{node.id}: " - f"credentials #{credentials_meta['id']} not available" - ) - - if event_filter_input_name: - # Shape of the event filter is enforced in Block.__init__ - event_filter = cast(dict, node.input_default[event_filter_input_name]) - events = [ - block.webhook_config.event_format.format(event=event) - for event, enabled in event_filter.items() - if enabled is True - ] - logger.debug(f"Webhook events to subscribe to: {', '.join(events)}") + if new_webhook: + node = await set_node_webhook(node.id, new_webhook.id) else: - events = [] - - # Find/make and attach a suitable webhook to the node - if auto_setup_webhook: - assert credentials is not None - new_webhook = await webhooks_manager.get_suitable_auto_webhook( - user_id, - credentials, - block.webhook_config.webhook_type, - resource, - events, + logger.debug( + f"Node #{node.id} does not have everything for a webhook: {feedback}" ) - else: - # Manual webhook -> no credentials -> don't register but do create - new_webhook = await webhooks_manager.get_manual_webhook( - user_id, - node.graph_id, - block.webhook_config.webhook_type, - events, - ) - logger.debug(f"Acquired webhook: {new_webhook}") - return await set_node_webhook(node.id, new_webhook.id) - else: - logger.debug(f"Node #{node.id} does not have everything for a webhook") return node async def on_node_deactivate( + user_id: str, node: "NodeModel", *, credentials: Optional["Credentials"] = None, @@ -233,7 +157,9 @@ async def on_node_deactivate( f"Pruning{' and deregistering' if credentials else ''} " f"webhook #{webhook.id}" ) - await webhooks_manager.prune_webhook_if_dangling(webhook.id, credentials) + await webhooks_manager.prune_webhook_if_dangling( + user_id, webhook.id, credentials + ) if ( cast(BlockSchema, block.input_schema).get_credentials_fields() and not credentials diff --git a/autogpt_platform/backend/backend/integrations/webhooks/utils.py b/autogpt_platform/backend/backend/integrations/webhooks/utils.py index e53a18f0fb..d7c66efe29 100644 --- a/autogpt_platform/backend/backend/integrations/webhooks/utils.py +++ b/autogpt_platform/backend/backend/integrations/webhooks/utils.py @@ -1,7 +1,22 @@ +import logging +from typing import TYPE_CHECKING, Optional, cast + +from pydantic import JsonValue + +from backend.integrations.creds_manager import IntegrationCredentialsManager from backend.integrations.providers import ProviderName from backend.util.settings import Config +from . import get_webhook_manager, supports_webhooks + +if TYPE_CHECKING: + from backend.data.block import Block, BlockSchema + from backend.data.integrations import Webhook + from backend.data.model import Credentials + +logger = logging.getLogger(__name__) app_config = Config() +credentials_manager = IntegrationCredentialsManager() # TODO: add test to assert this matches the actual API route @@ -10,3 +25,122 @@ def webhook_ingress_url(provider_name: ProviderName, webhook_id: str) -> str: f"{app_config.platform_base_url}/api/integrations/{provider_name.value}" f"/webhooks/{webhook_id}/ingress" ) + + +async def setup_webhook_for_block( + user_id: str, + trigger_block: "Block[BlockSchema, BlockSchema]", + trigger_config: dict[str, JsonValue], # = Trigger block inputs + for_graph_id: Optional[str] = None, + for_preset_id: Optional[str] = None, + credentials: Optional["Credentials"] = None, +) -> tuple["Webhook", None] | tuple[None, str]: + """ + Utility function to create (and auto-setup if possible) a webhook for a given provider. + + Returns: + Webhook: The created or found webhook object, if successful. + str: A feedback message, if any required inputs are missing. + """ + from backend.data.block import BlockWebhookConfig + + if not (trigger_base_config := trigger_block.webhook_config): + raise ValueError(f"Block #{trigger_block.id} does not have a webhook_config") + + provider = trigger_base_config.provider + if not supports_webhooks(provider): + raise NotImplementedError( + f"Block #{trigger_block.id} has webhook_config for provider {provider} " + "for which we do not have a WebhooksManager" + ) + + logger.debug( + f"Setting up webhook for block #{trigger_block.id} with config {trigger_config}" + ) + + # Check & parse the event filter input, if any + events: list[str] = [] + if event_filter_input_name := trigger_base_config.event_filter_input: + if not (event_filter := trigger_config.get(event_filter_input_name)): + return None, ( + f"Cannot set up {provider.value} webhook without event filter input: " + f"missing input for '{event_filter_input_name}'" + ) + elif not ( + # Shape of the event filter is enforced in Block.__init__ + any((event_filter := cast(dict[str, bool], event_filter)).values()) + ): + return None, ( + f"Cannot set up {provider.value} webhook without any enabled events " + f"in event filter input '{event_filter_input_name}'" + ) + + events = [ + trigger_base_config.event_format.format(event=event) + for event, enabled in event_filter.items() + if enabled is True + ] + logger.debug(f"Webhook events to subscribe to: {', '.join(events)}") + + # Check & process prerequisites for auto-setup webhooks + if auto_setup_webhook := isinstance(trigger_base_config, BlockWebhookConfig): + try: + resource = trigger_base_config.resource_format.format(**trigger_config) + except KeyError as missing_key: + return None, ( + f"Cannot auto-setup {provider.value} webhook without resource: " + f"missing input for '{missing_key}'" + ) + logger.debug( + f"Constructed resource string {resource} from input {trigger_config}" + ) + + creds_field_name = next( + # presence of this field is enforced in Block.__init__ + iter(trigger_block.input_schema.get_credentials_fields()) + ) + + if not ( + credentials_meta := cast(dict, trigger_config.get(creds_field_name, None)) + ): + return None, f"Cannot set up {provider.value} webhook without credentials" + elif not ( + credentials := credentials + or await credentials_manager.get(user_id, credentials_meta["id"]) + ): + raise ValueError( + f"Cannot set up {provider.value} webhook without credentials: " + f"credentials #{credentials_meta['id']} not found for user #{user_id}" + ) + elif credentials.provider != provider: + raise ValueError( + f"Credentials #{credentials.id} do not match provider {provider.value}" + ) + else: + # not relevant for manual webhooks: + resource = "" + credentials = None + + webhooks_manager = get_webhook_manager(provider) + + # Find/make and attach a suitable webhook to the node + if auto_setup_webhook: + assert credentials is not None + webhook = await webhooks_manager.get_suitable_auto_webhook( + user_id=user_id, + credentials=credentials, + webhook_type=trigger_base_config.webhook_type, + resource=resource, + events=events, + ) + else: + # Manual webhook -> no credentials -> don't register but do create + webhook = await webhooks_manager.get_manual_webhook( + user_id=user_id, + webhook_type=trigger_base_config.webhook_type, + events=events, + graph_id=for_graph_id, + preset_id=for_preset_id, + ) + logger.debug(f"Acquired webhook: {webhook}") + return webhook, None diff --git a/autogpt_platform/backend/test/server/test_con_manager.py b/autogpt_platform/backend/backend/server/conn_manager_test.py similarity index 100% rename from autogpt_platform/backend/test/server/test_con_manager.py rename to autogpt_platform/backend/backend/server/conn_manager_test.py diff --git a/autogpt_platform/backend/backend/server/integrations/router.py b/autogpt_platform/backend/backend/server/integrations/router.py index 26f33eba37..6deb4d77f7 100644 --- a/autogpt_platform/backend/backend/server/integrations/router.py +++ b/autogpt_platform/backend/backend/server/integrations/router.py @@ -2,11 +2,19 @@ import asyncio import logging from typing import TYPE_CHECKING, Annotated, Awaitable, Literal -from fastapi import APIRouter, Body, Depends, HTTPException, Path, Query, Request +from fastapi import ( + APIRouter, + Body, + Depends, + HTTPException, + Path, + Query, + Request, + status, +) from pydantic import BaseModel, Field -from starlette.status import HTTP_404_NOT_FOUND -from backend.data.graph import set_node_webhook +from backend.data.graph import get_graph, set_node_webhook from backend.data.integrations import ( WebhookEvent, get_all_webhooks_by_creds, @@ -14,12 +22,18 @@ from backend.data.integrations import ( publish_webhook_event, wait_for_webhook_event, ) -from backend.data.model import Credentials, CredentialsType, OAuth2Credentials +from backend.data.model import ( + Credentials, + CredentialsType, + HostScopedCredentials, + OAuth2Credentials, +) from backend.executor.utils import add_graph_execution from backend.integrations.creds_manager import IntegrationCredentialsManager from backend.integrations.oauth import HANDLERS_BY_NAME from backend.integrations.providers import ProviderName from backend.integrations.webhooks import get_webhook_manager +from backend.server.v2.library.db import set_preset_webhook, update_preset from backend.util.exceptions import NeedConfirmation, NotFoundError from backend.util.settings import Settings @@ -73,6 +87,9 @@ class CredentialsMetaResponse(BaseModel): title: str | None scopes: list[str] | None username: str | None + host: str | None = Field( + default=None, description="Host pattern for host-scoped credentials" + ) @router.post("/{provider}/callback") @@ -95,7 +112,10 @@ async def callback( if not valid_state: logger.warning(f"Invalid or expired state token for user {user_id}") - raise HTTPException(status_code=400, detail="Invalid or expired state token") + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail="Invalid or expired state token", + ) try: scopes = valid_state.scopes logger.debug(f"Retrieved scopes from state token: {scopes}") @@ -122,17 +142,12 @@ async def callback( ) except Exception as e: - logger.exception( - "OAuth callback for provider %s failed during code exchange: %s. Confirm provider credentials.", - provider.value, - e, + logger.error( + f"OAuth2 Code->Token exchange failed for provider {provider.value}: {e}" ) raise HTTPException( - status_code=400, - detail={ - "message": str(e), - "hint": "Verify OAuth configuration and try again.", - }, + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"OAuth2 callback failed to exchange code for tokens: {str(e)}", ) # TODO: Allow specifying `title` to set on `credentials` @@ -149,6 +164,9 @@ async def callback( title=credentials.title, scopes=credentials.scopes, username=credentials.username, + host=( + credentials.host if isinstance(credentials, HostScopedCredentials) else None + ), ) @@ -165,6 +183,7 @@ async def list_credentials( title=cred.title, scopes=cred.scopes if isinstance(cred, OAuth2Credentials) else None, username=cred.username if isinstance(cred, OAuth2Credentials) else None, + host=cred.host if isinstance(cred, HostScopedCredentials) else None, ) for cred in credentials ] @@ -186,6 +205,7 @@ async def list_credentials_by_provider( title=cred.title, scopes=cred.scopes if isinstance(cred, OAuth2Credentials) else None, username=cred.username if isinstance(cred, OAuth2Credentials) else None, + host=cred.host if isinstance(cred, HostScopedCredentials) else None, ) for cred in credentials ] @@ -201,10 +221,13 @@ async def get_credential( ) -> Credentials: credential = await creds_manager.get(user_id, cred_id) if not credential: - raise HTTPException(status_code=404, detail="Credentials not found") + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Credentials not found" + ) if credential.provider != provider: raise HTTPException( - status_code=404, detail="Credentials do not match the specified provider" + status_code=status.HTTP_404_NOT_FOUND, + detail="Credentials do not match the specified provider", ) return credential @@ -222,7 +245,8 @@ async def create_credentials( await creds_manager.create(user_id, credentials) except Exception as e: raise HTTPException( - status_code=500, detail=f"Failed to store credentials: {str(e)}" + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Failed to store credentials: {str(e)}", ) return credentials @@ -256,14 +280,17 @@ async def delete_credentials( ) -> CredentialsDeletionResponse | CredentialsDeletionNeedsConfirmationResponse: creds = await creds_manager.store.get_creds_by_id(user_id, cred_id) if not creds: - raise HTTPException(status_code=404, detail="Credentials not found") + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, detail="Credentials not found" + ) if creds.provider != provider: raise HTTPException( - status_code=404, detail="Credentials do not match the specified provider" + status_code=status.HTTP_404_NOT_FOUND, + detail="Credentials do not match the specified provider", ) try: - await remove_all_webhooks_for_credentials(creds, force) + await remove_all_webhooks_for_credentials(user_id, creds, force) except NeedConfirmation as e: return CredentialsDeletionNeedsConfirmationResponse(message=str(e)) @@ -294,16 +321,10 @@ async def webhook_ingress_generic( logger.debug(f"Received {provider.value} webhook ingress for ID {webhook_id}") webhook_manager = get_webhook_manager(provider) try: - webhook = await get_webhook(webhook_id) + webhook = await get_webhook(webhook_id, include_relations=True) except NotFoundError as e: - logger.warning( - "Webhook payload received for unknown webhook %s. Confirm the webhook ID.", - webhook_id, - ) - raise HTTPException( - status_code=HTTP_404_NOT_FOUND, - detail={"message": str(e), "hint": "Check if the webhook ID is correct."}, - ) from e + logger.warning(f"Webhook payload received for unknown webhook #{webhook_id}") + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e)) logger.debug(f"Webhook #{webhook_id}: {webhook}") payload, event_type = await webhook_manager.validate_payload(webhook, request) logger.debug( @@ -320,11 +341,11 @@ async def webhook_ingress_generic( await publish_webhook_event(webhook_event) logger.debug(f"Webhook event published: {webhook_event}") - if not webhook.attached_nodes: + if not (webhook.triggered_nodes or webhook.triggered_presets): return executions: list[Awaitable] = [] - for node in webhook.attached_nodes: + for node in webhook.triggered_nodes: logger.debug(f"Webhook-attached node: {node}") if not node.is_triggered_by_event_type(event_type): logger.debug(f"Node #{node.id} doesn't trigger on event {event_type}") @@ -335,7 +356,48 @@ async def webhook_ingress_generic( user_id=webhook.user_id, graph_id=node.graph_id, graph_version=node.graph_version, - inputs={f"webhook_{webhook_id}_payload": payload}, + nodes_input_masks={node.id: {"payload": payload}}, + ) + ) + for preset in webhook.triggered_presets: + logger.debug(f"Webhook-attached preset: {preset}") + if not preset.is_active: + logger.debug(f"Preset #{preset.id} is inactive") + continue + + graph = await get_graph(preset.graph_id, preset.graph_version, webhook.user_id) + if not graph: + logger.error( + f"User #{webhook.user_id} has preset #{preset.id} for graph " + f"#{preset.graph_id} v{preset.graph_version}, " + "but no access to the graph itself." + ) + logger.info(f"Automatically deactivating broken preset #{preset.id}") + await update_preset(preset.user_id, preset.id, is_active=False) + continue + if not (trigger_node := graph.webhook_input_node): + # NOTE: this should NEVER happen, but we log and handle it gracefully + logger.error( + f"Preset #{preset.id} is triggered by webhook #{webhook.id}, but graph " + f"#{preset.graph_id} v{preset.graph_version} has no webhook input node" + ) + await set_preset_webhook(preset.user_id, preset.id, None) + continue + if not trigger_node.block.is_triggered_by_event_type(preset.inputs, event_type): + logger.debug(f"Preset #{preset.id} doesn't trigger on event {event_type}") + continue + logger.debug(f"Executing preset #{preset.id} for webhook #{webhook.id}") + + executions.append( + add_graph_execution( + user_id=webhook.user_id, + graph_id=preset.graph_id, + preset_id=preset.id, + graph_version=preset.graph_version, + graph_credentials_inputs=preset.credentials, + nodes_input_masks={ + trigger_node.id: {**preset.inputs, "payload": payload} + }, ) ) asyncio.gather(*executions) @@ -360,7 +422,9 @@ async def webhook_ping( return False if not await wait_for_webhook_event(webhook_id, event_type="ping", timeout=10): - raise HTTPException(status_code=504, detail="Webhook ping timed out") + raise HTTPException( + status_code=status.HTTP_504_GATEWAY_TIMEOUT, detail="Webhook ping timed out" + ) return True @@ -369,32 +433,37 @@ async def webhook_ping( async def remove_all_webhooks_for_credentials( - credentials: Credentials, force: bool = False + user_id: str, credentials: Credentials, force: bool = False ) -> None: """ Remove and deregister all webhooks that were registered using the given credentials. Params: + user_id: The ID of the user who owns the credentials and webhooks. credentials: The credentials for which to remove the associated webhooks. force: Whether to proceed if any of the webhooks are still in use. Raises: NeedConfirmation: If any of the webhooks are still in use and `force` is `False` """ - webhooks = await get_all_webhooks_by_creds(credentials.id) - if any(w.attached_nodes for w in webhooks) and not force: + webhooks = await get_all_webhooks_by_creds( + user_id, credentials.id, include_relations=True + ) + if any(w.triggered_nodes or w.triggered_presets for w in webhooks) and not force: raise NeedConfirmation( "Some webhooks linked to these credentials are still in use by an agent" ) for webhook in webhooks: - # Unlink all nodes - for node in webhook.attached_nodes or []: + # Unlink all nodes & presets + for node in webhook.triggered_nodes: await set_node_webhook(node.id, None) + for preset in webhook.triggered_presets: + await set_preset_webhook(user_id, preset.id, None) # Prune the webhook webhook_manager = get_webhook_manager(ProviderName(credentials.provider)) success = await webhook_manager.prune_webhook_if_dangling( - webhook.id, credentials + user_id, webhook.id, credentials ) if not success: logger.warning(f"Webhook #{webhook.id} failed to prune") @@ -405,7 +474,7 @@ def _get_provider_oauth_handler( ) -> "BaseOAuthHandler": if provider_name not in HANDLERS_BY_NAME: raise HTTPException( - status_code=404, + status_code=status.HTTP_404_NOT_FOUND, detail=f"Provider '{provider_name.value}' does not support OAuth", ) @@ -413,14 +482,13 @@ def _get_provider_oauth_handler( client_secret = getattr(settings.secrets, f"{provider_name.value}_client_secret") if not (client_id and client_secret): logger.error( - "OAuth credentials for provider %s are missing. Check environment configuration.", - provider_name.value, + f"Attempt to use unconfigured {provider_name.value} OAuth integration" ) raise HTTPException( - status_code=501, + status_code=status.HTTP_501_NOT_IMPLEMENTED, detail={ - "message": f"Integration with provider '{provider_name.value}' is not configured", - "hint": "Set client ID and secret in the environment.", + "message": f"Integration with provider '{provider_name.value}' is not configured.", + "hint": "Set client ID and secret in the application's deployment environment", }, ) diff --git a/autogpt_platform/backend/backend/server/rest_api.py b/autogpt_platform/backend/backend/server/rest_api.py index f18bd932f3..0ab7b9ac56 100644 --- a/autogpt_platform/backend/backend/server/rest_api.py +++ b/autogpt_platform/backend/backend/server/rest_api.py @@ -279,6 +279,7 @@ class AgentServer(backend.util.service.AppProcess): @staticmethod async def test_delete_graph(graph_id: str, user_id: str): + """Used for clean-up after a test run""" await backend.server.v2.library.db.delete_library_agent_by_graph_id( graph_id=graph_id, user_id=user_id ) @@ -323,18 +324,14 @@ class AgentServer(backend.util.service.AppProcess): @staticmethod async def test_execute_preset( - graph_id: str, - graph_version: int, preset_id: str, user_id: str, - node_input: Optional[dict[str, Any]] = None, + inputs: Optional[dict[str, Any]] = None, ): return await backend.server.v2.library.routes.presets.execute_preset( - graph_id=graph_id, - graph_version=graph_version, preset_id=preset_id, - node_input=node_input or {}, user_id=user_id, + inputs=inputs or {}, ) @staticmethod @@ -360,11 +357,22 @@ class AgentServer(backend.util.service.AppProcess): provider: ProviderName, credentials: Credentials, ) -> Credentials: - from backend.server.integrations.router import create_credentials - - return await create_credentials( - user_id=user_id, provider=provider, credentials=credentials + from backend.server.integrations.router import ( + create_credentials, + get_credential, ) + try: + return await create_credentials( + user_id=user_id, provider=provider, credentials=credentials + ) + except Exception as e: + logger.error(f"Error creating credentials: {e}") + return await get_credential( + provider=provider, + user_id=user_id, + cred_id=credentials.id, + ) + def set_test_dependency_overrides(self, overrides: dict): app.dependency_overrides.update(overrides) diff --git a/autogpt_platform/backend/backend/server/routers/analytics.py b/autogpt_platform/backend/backend/server/routers/analytics.py index b6116391d9..cf0ae85723 100644 --- a/autogpt_platform/backend/backend/server/routers/analytics.py +++ b/autogpt_platform/backend/backend/server/routers/analytics.py @@ -4,6 +4,7 @@ import logging from typing import Annotated import fastapi +import pydantic import backend.data.analytics from backend.server.utils import get_user_id @@ -12,24 +13,28 @@ router = fastapi.APIRouter() logger = logging.getLogger(__name__) +class LogRawMetricRequest(pydantic.BaseModel): + metric_name: str = pydantic.Field(..., min_length=1) + metric_value: float = pydantic.Field(..., allow_inf_nan=False) + data_string: str = pydantic.Field(..., min_length=1) + + @router.post(path="/log_raw_metric") async def log_raw_metric( user_id: Annotated[str, fastapi.Depends(get_user_id)], - metric_name: Annotated[str, fastapi.Body(..., embed=True)], - metric_value: Annotated[float, fastapi.Body(..., embed=True)], - data_string: Annotated[str, fastapi.Body(..., embed=True)], + request: LogRawMetricRequest, ): try: result = await backend.data.analytics.log_raw_metric( user_id=user_id, - metric_name=metric_name, - metric_value=metric_value, - data_string=data_string, + metric_name=request.metric_name, + metric_value=request.metric_value, + data_string=request.data_string, ) return result.id except Exception as e: logger.exception( - "Failed to log metric %s for user %s: %s", metric_name, user_id, e + "Failed to log metric %s for user %s: %s", request.metric_name, user_id, e ) raise fastapi.HTTPException( status_code=500, diff --git a/autogpt_platform/backend/backend/server/routers/analytics_improved_test.py b/autogpt_platform/backend/backend/server/routers/analytics_improved_test.py index 9ec8af4942..1750574142 100644 --- a/autogpt_platform/backend/backend/server/routers/analytics_improved_test.py +++ b/autogpt_platform/backend/backend/server/routers/analytics_improved_test.py @@ -97,8 +97,17 @@ def test_log_raw_metric_invalid_request_improved() -> None: assert "data_string" in error_fields, "Should report missing data_string" -def test_log_raw_metric_type_validation_improved() -> None: +def test_log_raw_metric_type_validation_improved( + mocker: pytest_mock.MockFixture, +) -> None: """Test metric type validation with improved assertions.""" + # Mock the analytics function to avoid event loop issues + mocker.patch( + "backend.data.analytics.log_raw_metric", + new_callable=AsyncMock, + return_value=Mock(id="test-id"), + ) + invalid_requests = [ { "data": { @@ -119,10 +128,10 @@ def test_log_raw_metric_type_validation_improved() -> None: { "data": { "metric_name": "test", - "metric_value": float("inf"), # Infinity - "data_string": "test", + "metric_value": 123, # Valid number + "data_string": "", # Empty data_string }, - "expected_error": "ensure this value is finite", + "expected_error": "String should have at least 1 character", }, ] diff --git a/autogpt_platform/backend/backend/server/routers/analytics_parametrized_test.py b/autogpt_platform/backend/backend/server/routers/analytics_parametrized_test.py index 58d6733142..a1250db942 100644 --- a/autogpt_platform/backend/backend/server/routers/analytics_parametrized_test.py +++ b/autogpt_platform/backend/backend/server/routers/analytics_parametrized_test.py @@ -93,10 +93,18 @@ def test_log_raw_metric_values_parametrized( ], ) def test_log_raw_metric_invalid_requests_parametrized( + mocker: pytest_mock.MockFixture, invalid_data: dict, expected_error: str, ) -> None: """Test invalid metric requests with parametrize.""" + # Mock the analytics function to avoid event loop issues + mocker.patch( + "backend.data.analytics.log_raw_metric", + new_callable=AsyncMock, + return_value=Mock(id="test-id"), + ) + response = client.post("/log_raw_metric", json=invalid_data) assert response.status_code == 422 diff --git a/autogpt_platform/backend/backend/server/routers/v1.py b/autogpt_platform/backend/backend/server/routers/v1.py index e177d22d34..19b8ef07e5 100644 --- a/autogpt_platform/backend/backend/server/routers/v1.py +++ b/autogpt_platform/backend/backend/server/routers/v1.py @@ -9,7 +9,7 @@ import stripe from autogpt_libs.auth.middleware import auth_middleware from autogpt_libs.feature_flag.client import feature_flag from autogpt_libs.utils.cache import thread_cached -from fastapi import APIRouter, Body, Depends, HTTPException, Request, Response +from fastapi import APIRouter, Body, Depends, HTTPException, Path, Request, Response from starlette.status import HTTP_204_NO_CONTENT, HTTP_404_NOT_FOUND from typing_extensions import Optional, TypedDict @@ -72,6 +72,7 @@ from backend.server.model import ( UpdatePermissionsRequest, ) from backend.server.utils import get_user_id +from backend.util.exceptions import NotFoundError from backend.util.service import get_service_client from backend.util.settings import Settings @@ -765,70 +766,94 @@ async def delete_graph_execution( class ScheduleCreationRequest(pydantic.BaseModel): + graph_version: Optional[int] = None + name: str cron: str - input_data: dict[Any, Any] - graph_id: str - graph_version: int + inputs: dict[str, Any] + credentials: dict[str, CredentialsMetaInput] = pydantic.Field(default_factory=dict) @v1_router.post( - path="/schedules", + path="/graphs/{graph_id}/schedules", summary="Create execution schedule", tags=["schedules"], dependencies=[Depends(auth_middleware)], ) -async def create_schedule( +async def create_graph_execution_schedule( user_id: Annotated[str, Depends(get_user_id)], - schedule: ScheduleCreationRequest, + graph_id: str = Path(..., description="ID of the graph to schedule"), + schedule_params: ScheduleCreationRequest = Body(), ) -> scheduler.GraphExecutionJobInfo: graph = await graph_db.get_graph( - schedule.graph_id, schedule.graph_version, user_id=user_id + graph_id=graph_id, + version=schedule_params.graph_version, + user_id=user_id, ) if not graph: raise HTTPException( status_code=404, - detail=f"Graph #{schedule.graph_id} v.{schedule.graph_version} not found.", + detail=f"Graph #{graph_id} v{schedule_params.graph_version} not found.", ) return await execution_scheduler_client().add_execution_schedule( - graph_id=schedule.graph_id, - graph_version=graph.version, - cron=schedule.cron, - input_data=schedule.input_data, user_id=user_id, + graph_id=graph_id, + graph_version=graph.version, + name=schedule_params.name, + cron=schedule_params.cron, + input_data=schedule_params.inputs, + input_credentials=schedule_params.credentials, ) +@v1_router.get( + path="/graphs/{graph_id}/schedules", + summary="List execution schedules for a graph", + tags=["schedules"], + dependencies=[Depends(auth_middleware)], +) +async def list_graph_execution_schedules( + user_id: Annotated[str, Depends(get_user_id)], + graph_id: str = Path(), +) -> list[scheduler.GraphExecutionJobInfo]: + return await execution_scheduler_client().get_execution_schedules( + user_id=user_id, + graph_id=graph_id, + ) + + +@v1_router.get( + path="/schedules", + summary="List execution schedules for a user", + tags=["schedules"], + dependencies=[Depends(auth_middleware)], +) +async def list_all_graphs_execution_schedules( + user_id: Annotated[str, Depends(get_user_id)], +) -> list[scheduler.GraphExecutionJobInfo]: + return await execution_scheduler_client().get_execution_schedules(user_id=user_id) + + @v1_router.delete( path="/schedules/{schedule_id}", summary="Delete execution schedule", tags=["schedules"], dependencies=[Depends(auth_middleware)], ) -async def delete_schedule( - schedule_id: str, +async def delete_graph_execution_schedule( user_id: Annotated[str, Depends(get_user_id)], -) -> dict[Any, Any]: - await execution_scheduler_client().delete_schedule(schedule_id, user_id=user_id) + schedule_id: str = Path(..., description="ID of the schedule to delete"), +) -> dict[str, Any]: + try: + await execution_scheduler_client().delete_schedule(schedule_id, user_id=user_id) + except NotFoundError: + raise HTTPException( + status_code=HTTP_404_NOT_FOUND, + detail=f"Schedule #{schedule_id} not found", + ) return {"id": schedule_id} -@v1_router.get( - path="/schedules", - summary="List execution schedules", - tags=["schedules"], - dependencies=[Depends(auth_middleware)], -) -async def get_execution_schedules( - user_id: Annotated[str, Depends(get_user_id)], - graph_id: str | None = None, -) -> list[scheduler.GraphExecutionJobInfo]: - return await execution_scheduler_client().get_execution_schedules( - user_id=user_id, - graph_id=graph_id, - ) - - ######################################################## ##################### API KEY ############################## ######################################################## diff --git a/autogpt_platform/backend/backend/server/test_fixtures.py b/autogpt_platform/backend/backend/server/test_fixtures.py index a73f5edfb4..f787724649 100644 --- a/autogpt_platform/backend/backend/server/test_fixtures.py +++ b/autogpt_platform/backend/backend/server/test_fixtures.py @@ -108,11 +108,16 @@ class TestDatabaseIsolation: where={"email": {"contains": "@test.example"}} ) + @pytest.fixture(scope="session") async def test_create_user(self, test_db_connection): """Test that demonstrates proper isolation.""" # This test has access to a clean database user = await test_db_connection.user.create( - data={"email": "test@test.example", "name": "Test User"} + data={ + "id": "test-user-id", + "email": "test@test.example", + "name": "Test User", + } ) assert user.email == "test@test.example" # User will be cleaned up automatically diff --git a/autogpt_platform/backend/backend/server/v2/library/db.py b/autogpt_platform/backend/backend/server/v2/library/db.py index b0f8f7a755..454bce9b07 100644 --- a/autogpt_platform/backend/backend/server/v2/library/db.py +++ b/autogpt_platform/backend/backend/server/v2/library/db.py @@ -1,5 +1,5 @@ import logging -from typing import Optional +from typing import Literal, Optional import fastapi import prisma.errors @@ -7,17 +7,17 @@ import prisma.fields import prisma.models import prisma.types -import backend.data.graph +import backend.data.graph as graph_db import backend.server.model import backend.server.v2.library.model as library_model import backend.server.v2.store.exceptions as store_exceptions import backend.server.v2.store.image_gen as store_image_gen import backend.server.v2.store.media as store_media -from backend.data import db -from backend.data import graph as graph_db -from backend.data.db import locked_transaction +from backend.data.block import BlockInput +from backend.data.db import locked_transaction, transaction from backend.data.execution import get_graph_execution from backend.data.includes import library_agent_include +from backend.data.model import CredentialsMetaInput from backend.integrations.creds_manager import IntegrationCredentialsManager from backend.integrations.webhooks.graph_lifecycle_hooks import on_graph_activate from backend.util.exceptions import NotFoundError @@ -122,7 +122,7 @@ async def list_library_agents( except Exception as e: # Skip this agent if there was an error logger.error( - f"Error parsing LibraryAgent when getting library agents from db: {e}" + f"Error parsing LibraryAgent #{agent.id} from DB item: {e}" ) continue @@ -168,7 +168,7 @@ async def get_library_agent(id: str, user_id: str) -> library_model.LibraryAgent ) if not library_agent: - raise store_exceptions.AgentNotFoundError(f"Library agent #{id} not found") + raise NotFoundError(f"Library agent #{id} not found") return library_model.LibraryAgent.from_db(library_agent) @@ -215,8 +215,34 @@ async def get_library_agent_by_store_version_id( return None +async def get_library_agent_by_graph_id( + user_id: str, + graph_id: str, + graph_version: Optional[int] = None, +) -> library_model.LibraryAgent | None: + try: + filter: prisma.types.LibraryAgentWhereInput = { + "agentGraphId": graph_id, + "userId": user_id, + "isDeleted": False, + } + if graph_version is not None: + filter["agentGraphVersion"] = graph_version + + agent = await prisma.models.LibraryAgent.prisma().find_first( + where=filter, + include=library_agent_include(user_id), + ) + if not agent: + return None + return library_model.LibraryAgent.from_db(agent) + except prisma.errors.PrismaError as e: + logger.error(f"Database error fetching library agent by graph ID: {e}") + raise store_exceptions.DatabaseError("Failed to fetch library agent") from e + + async def add_generated_agent_image( - graph: backend.data.graph.GraphModel, + graph: graph_db.GraphModel, library_agent_id: str, ) -> Optional[prisma.models.LibraryAgent]: """ @@ -249,7 +275,7 @@ async def add_generated_agent_image( async def create_library_agent( - graph: backend.data.graph.GraphModel, + graph: graph_db.GraphModel, user_id: str, ) -> library_model.LibraryAgent: """ @@ -346,8 +372,8 @@ async def update_library_agent( auto_update_version: Optional[bool] = None, is_favorite: Optional[bool] = None, is_archived: Optional[bool] = None, - is_deleted: Optional[bool] = None, -) -> None: + is_deleted: Optional[Literal[False]] = None, +) -> library_model.LibraryAgent: """ Updates the specified LibraryAgent record. @@ -357,15 +383,18 @@ async def update_library_agent( auto_update_version: Whether the agent should auto-update to active version. is_favorite: Whether this agent is marked as a favorite. is_archived: Whether this agent is archived. - is_deleted: Whether this agent is deleted. + + Returns: + The updated LibraryAgent. Raises: + NotFoundError: If the specified LibraryAgent does not exist. DatabaseError: If there's an error in the update operation. """ logger.debug( f"Updating library agent {library_agent_id} for user {user_id} with " f"auto_update_version={auto_update_version}, is_favorite={is_favorite}, " - f"is_archived={is_archived}, is_deleted={is_deleted}" + f"is_archived={is_archived}" ) update_fields: prisma.types.LibraryAgentUpdateManyMutationInput = {} if auto_update_version is not None: @@ -375,17 +404,46 @@ async def update_library_agent( if is_archived is not None: update_fields["isArchived"] = is_archived if is_deleted is not None: + if is_deleted is True: + raise RuntimeError( + "Use delete_library_agent() to (soft-)delete library agents" + ) update_fields["isDeleted"] = is_deleted + if not update_fields: + raise ValueError("No values were passed to update") try: - await prisma.models.LibraryAgent.prisma().update_many( - where={"id": library_agent_id, "userId": user_id}, data=update_fields + n_updated = await prisma.models.LibraryAgent.prisma().update_many( + where={"id": library_agent_id, "userId": user_id}, + data=update_fields, + ) + if n_updated < 1: + raise NotFoundError(f"Library agent {library_agent_id} not found") + + return await get_library_agent( + id=library_agent_id, + user_id=user_id, ) except prisma.errors.PrismaError as e: logger.error(f"Database error updating library agent: {str(e)}") raise store_exceptions.DatabaseError("Failed to update library agent") from e +async def delete_library_agent( + library_agent_id: str, user_id: str, soft_delete: bool = True +) -> None: + if soft_delete: + deleted_count = await prisma.models.LibraryAgent.prisma().update_many( + where={"id": library_agent_id, "userId": user_id}, data={"isDeleted": True} + ) + else: + deleted_count = await prisma.models.LibraryAgent.prisma().delete_many( + where={"id": library_agent_id, "userId": user_id} + ) + if deleted_count < 1: + raise NotFoundError(f"Library agent #{library_agent_id} not found") + + async def delete_library_agent_by_graph_id(graph_id: str, user_id: str) -> None: """ Deletes a library agent for the given user @@ -525,7 +583,10 @@ async def list_presets( ) raise store_exceptions.DatabaseError("Invalid pagination parameters") - query_filter: prisma.types.AgentPresetWhereInput = {"userId": user_id} + query_filter: prisma.types.AgentPresetWhereInput = { + "userId": user_id, + "isDeleted": False, + } if graph_id: query_filter["agentGraphId"] = graph_id @@ -581,7 +642,7 @@ async def get_preset( where={"id": preset_id}, include={"InputPresets": True}, ) - if not preset or preset.userId != user_id: + if not preset or preset.userId != user_id or preset.isDeleted: return None return library_model.LibraryAgentPreset.from_db(preset) except prisma.errors.PrismaError as e: @@ -618,12 +679,19 @@ async def create_preset( agentGraphId=preset.graph_id, agentGraphVersion=preset.graph_version, isActive=preset.is_active, + webhookId=preset.webhook_id, InputPresets={ "create": [ prisma.types.AgentNodeExecutionInputOutputCreateWithoutRelationsInput( # noqa name=name, data=prisma.fields.Json(data) ) - for name, data in preset.inputs.items() + for name, data in { + **preset.inputs, + **{ + key: creds_meta.model_dump(exclude_none=True) + for key, creds_meta in preset.credentials.items() + }, + }.items() ] }, ), @@ -664,6 +732,7 @@ async def create_preset_from_graph_execution( user_id=user_id, preset=library_model.LibraryAgentPresetCreatable( inputs=graph_execution.inputs, + credentials={}, # FIXME graph_id=graph_execution.graph_id, graph_version=graph_execution.graph_version, name=create_request.name, @@ -676,7 +745,11 @@ async def create_preset_from_graph_execution( async def update_preset( user_id: str, preset_id: str, - preset: library_model.LibraryAgentPresetUpdatable, + inputs: Optional[BlockInput] = None, + credentials: Optional[dict[str, CredentialsMetaInput]] = None, + name: Optional[str] = None, + description: Optional[str] = None, + is_active: Optional[bool] = None, ) -> library_model.LibraryAgentPreset: """ Updates an existing AgentPreset for a user. @@ -684,49 +757,95 @@ async def update_preset( Args: user_id: The ID of the user updating the preset. preset_id: The ID of the preset to update. - preset: The preset data used for the update. + inputs: New inputs object to set on the preset. + credentials: New credentials to set on the preset. + name: New name for the preset. + description: New description for the preset. + is_active: New active status for the preset. Returns: The updated LibraryAgentPreset. Raises: DatabaseError: If there's a database error in updating the preset. - ValueError: If attempting to update a non-existent preset. + NotFoundError: If attempting to update a non-existent preset. """ + current = await get_preset(user_id, preset_id) # assert ownership + if not current: + raise NotFoundError(f"Preset #{preset_id} not found for user #{user_id}") logger.debug( - f"Updating preset #{preset_id} ({repr(preset.name)}) for user #{user_id}", + f"Updating preset #{preset_id} ({repr(current.name)}) for user #{user_id}", ) try: - update_data: prisma.types.AgentPresetUpdateInput = {} - if preset.name: - update_data["name"] = preset.name - if preset.description: - update_data["description"] = preset.description - if preset.inputs: - update_data["InputPresets"] = { - "create": [ - prisma.types.AgentNodeExecutionInputOutputCreateWithoutRelationsInput( # noqa - name=name, data=prisma.fields.Json(data) + async with transaction() as tx: + update_data: prisma.types.AgentPresetUpdateInput = {} + if name: + update_data["name"] = name + if description: + update_data["description"] = description + if is_active is not None: + update_data["isActive"] = is_active + if inputs or credentials: + if not (inputs and credentials): + raise ValueError( + "Preset inputs and credentials must be provided together" ) - for name, data in preset.inputs.items() - ] - } - if preset.is_active: - update_data["isActive"] = preset.is_active + update_data["InputPresets"] = { + "create": [ + prisma.types.AgentNodeExecutionInputOutputCreateWithoutRelationsInput( # noqa + name=name, data=prisma.fields.Json(data) + ) + for name, data in { + **inputs, + **{ + key: creds_meta.model_dump(exclude_none=True) + for key, creds_meta in credentials.items() + }, + }.items() + ], + } + # Existing InputPresets must be deleted, in a separate query + await prisma.models.AgentNodeExecutionInputOutput.prisma( + tx + ).delete_many(where={"agentPresetId": preset_id}) - updated = await prisma.models.AgentPreset.prisma().update( - where={"id": preset_id}, - data=update_data, - include={"InputPresets": True}, - ) + updated = await prisma.models.AgentPreset.prisma(tx).update( + where={"id": preset_id}, + data=update_data, + include={"InputPresets": True}, + ) if not updated: - raise ValueError(f"AgentPreset #{preset_id} not found") + raise RuntimeError(f"AgentPreset #{preset_id} vanished while updating") return library_model.LibraryAgentPreset.from_db(updated) except prisma.errors.PrismaError as e: logger.error(f"Database error updating preset: {e}") raise store_exceptions.DatabaseError("Failed to update preset") from e +async def set_preset_webhook( + user_id: str, preset_id: str, webhook_id: str | None +) -> library_model.LibraryAgentPreset: + current = await prisma.models.AgentPreset.prisma().find_unique( + where={"id": preset_id}, + include={"InputPresets": True}, + ) + if not current or current.userId != user_id: + raise NotFoundError(f"Preset #{preset_id} not found") + + updated = await prisma.models.AgentPreset.prisma().update( + where={"id": preset_id}, + data=( + {"Webhook": {"connect": {"id": webhook_id}}} + if webhook_id + else {"Webhook": {"disconnect": True}} + ), + include={"InputPresets": True}, + ) + if not updated: + raise RuntimeError(f"AgentPreset #{preset_id} vanished while updating") + return library_model.LibraryAgentPreset.from_db(updated) + + async def delete_preset(user_id: str, preset_id: str) -> None: """ Soft-deletes a preset by marking it as isDeleted = True. @@ -738,7 +857,7 @@ async def delete_preset(user_id: str, preset_id: str) -> None: Raises: DatabaseError: If there's a database error during deletion. """ - logger.info(f"Deleting preset {preset_id} for user {user_id}") + logger.debug(f"Setting preset #{preset_id} for user #{user_id} to deleted") try: await prisma.models.AgentPreset.prisma().update_many( where={"id": preset_id, "userId": user_id}, @@ -765,7 +884,7 @@ async def fork_library_agent(library_agent_id: str, user_id: str): """ logger.debug(f"Forking library agent {library_agent_id} for user {user_id}") try: - async with db.locked_transaction(f"usr_trx_{user_id}-fork_agent"): + async with locked_transaction(f"usr_trx_{user_id}-fork_agent"): # Fetch the original agent original_agent = await get_library_agent(library_agent_id, user_id) diff --git a/autogpt_platform/backend/backend/server/v2/library/db_test.py b/autogpt_platform/backend/backend/server/v2/library/db_test.py index 5854c2ecb7..0b39b384ea 100644 --- a/autogpt_platform/backend/backend/server/v2/library/db_test.py +++ b/autogpt_platform/backend/backend/server/v2/library/db_test.py @@ -143,7 +143,7 @@ async def test_add_agent_to_library(mocker): ) mock_library_agent = mocker.patch("prisma.models.LibraryAgent.prisma") - mock_library_agent.return_value.find_first = mocker.AsyncMock(return_value=None) + mock_library_agent.return_value.find_unique = mocker.AsyncMock(return_value=None) mock_library_agent.return_value.create = mocker.AsyncMock( return_value=mock_library_agent_data ) @@ -159,21 +159,24 @@ async def test_add_agent_to_library(mocker): mock_store_listing_version.return_value.find_unique.assert_called_once_with( where={"id": "version123"}, include={"AgentGraph": True} ) - mock_library_agent.return_value.find_first.assert_called_once_with( + mock_library_agent.return_value.find_unique.assert_called_once_with( where={ - "userId": "test-user", - "agentGraphId": "agent1", - "agentGraphVersion": 1, + "userId_agentGraphId_agentGraphVersion": { + "userId": "test-user", + "agentGraphId": "agent1", + "agentGraphVersion": 1, + } }, - include=library_agent_include("test-user"), + include={"AgentGraph": True}, ) mock_library_agent.return_value.create.assert_called_once_with( - data=prisma.types.LibraryAgentCreateInput( - userId="test-user", - agentGraphId="agent1", - agentGraphVersion=1, - isCreatedByUser=False, - ), + data={ + "User": {"connect": {"id": "test-user"}}, + "AgentGraph": { + "connect": {"graphVersionId": {"id": "agent1", "version": 1}} + }, + "isCreatedByUser": False, + }, include=library_agent_include("test-user"), ) diff --git a/autogpt_platform/backend/backend/server/v2/library/model.py b/autogpt_platform/backend/backend/server/v2/library/model.py index 8c39f3dc3b..6b2b74d247 100644 --- a/autogpt_platform/backend/backend/server/v2/library/model.py +++ b/autogpt_platform/backend/backend/server/v2/library/model.py @@ -9,6 +9,8 @@ import pydantic import backend.data.block as block_model import backend.data.graph as graph_model import backend.server.model as server_model +from backend.data.model import CredentialsMetaInput, is_credentials_field_name +from backend.integrations.providers import ProviderName class LibraryAgentStatus(str, Enum): @@ -18,6 +20,14 @@ class LibraryAgentStatus(str, Enum): ERROR = "ERROR" # Agent is in an error state +class LibraryAgentTriggerInfo(pydantic.BaseModel): + provider: ProviderName + config_schema: dict[str, Any] = pydantic.Field( + description="Input schema for the trigger block" + ) + credentials_input_name: Optional[str] + + class LibraryAgent(pydantic.BaseModel): """ Represents an agent in the library, including metadata for display and @@ -40,8 +50,15 @@ class LibraryAgent(pydantic.BaseModel): name: str description: str - # Made input_schema and output_schema match GraphMeta's type input_schema: dict[str, Any] # Should be BlockIOObjectSubSchema in frontend + credentials_input_schema: dict[str, Any] = pydantic.Field( + description="Input schema for credentials required by the agent", + ) + + has_external_trigger: bool = pydantic.Field( + description="Whether the agent has an external trigger (e.g. webhook) node" + ) + trigger_setup_info: Optional[LibraryAgentTriggerInfo] = None # Indicates whether there's a new output (based on recent runs) new_output: bool @@ -106,6 +123,32 @@ class LibraryAgent(pydantic.BaseModel): name=graph.name, description=graph.description, input_schema=graph.input_schema, + credentials_input_schema=graph.credentials_input_schema, + has_external_trigger=graph.has_webhook_trigger, + trigger_setup_info=( + LibraryAgentTriggerInfo( + provider=trigger_block.webhook_config.provider, + config_schema={ + **(json_schema := trigger_block.input_schema.jsonschema()), + "properties": { + pn: sub_schema + for pn, sub_schema in json_schema["properties"].items() + if not is_credentials_field_name(pn) + }, + "required": [ + pn + for pn in json_schema.get("required", []) + if not is_credentials_field_name(pn) + ], + }, + credentials_input_name=next( + iter(trigger_block.input_schema.get_credentials_fields()), None + ), + ) + if graph.webhook_input_node + and (trigger_block := graph.webhook_input_node.block).webhook_config + else None + ), new_output=new_output, can_access_graph=can_access_graph, is_latest_version=is_latest_version, @@ -177,12 +220,15 @@ class LibraryAgentPresetCreatable(pydantic.BaseModel): graph_version: int inputs: block_model.BlockInput + credentials: dict[str, CredentialsMetaInput] name: str description: str is_active: bool = True + webhook_id: Optional[str] = None + class LibraryAgentPresetCreatableFromGraphExecution(pydantic.BaseModel): """ @@ -203,6 +249,7 @@ class LibraryAgentPresetUpdatable(pydantic.BaseModel): """ inputs: Optional[block_model.BlockInput] = None + credentials: Optional[dict[str, CredentialsMetaInput]] = None name: Optional[str] = None description: Optional[str] = None @@ -214,20 +261,28 @@ class LibraryAgentPreset(LibraryAgentPresetCreatable): """Represents a preset configuration for a library agent.""" id: str + user_id: str updated_at: datetime.datetime @classmethod def from_db(cls, preset: prisma.models.AgentPreset) -> "LibraryAgentPreset": if preset.InputPresets is None: - raise ValueError("Input values must be included in object") + raise ValueError("InputPresets must be included in AgentPreset query") input_data: block_model.BlockInput = {} + input_credentials: dict[str, CredentialsMetaInput] = {} for preset_input in preset.InputPresets: - input_data[preset_input.name] = preset_input.data + if not is_credentials_field_name(preset_input.name): + input_data[preset_input.name] = preset_input.data + else: + input_credentials[preset_input.name] = ( + CredentialsMetaInput.model_validate(preset_input.data) + ) return cls( id=preset.id, + user_id=preset.userId, updated_at=preset.updatedAt, graph_id=preset.agentGraphId, graph_version=preset.agentGraphVersion, @@ -235,6 +290,8 @@ class LibraryAgentPreset(LibraryAgentPresetCreatable): description=preset.description, is_active=preset.isActive, inputs=input_data, + credentials=input_credentials, + webhook_id=preset.webhookId, ) @@ -276,6 +333,3 @@ class LibraryAgentUpdateRequest(pydantic.BaseModel): is_archived: Optional[bool] = pydantic.Field( default=None, description="Archive the agent" ) - is_deleted: Optional[bool] = pydantic.Field( - default=None, description="Delete the agent" - ) diff --git a/autogpt_platform/backend/backend/server/v2/library/routes/agents.py b/autogpt_platform/backend/backend/server/v2/library/routes/agents.py index 54a0cd235f..899a6f87d4 100644 --- a/autogpt_platform/backend/backend/server/v2/library/routes/agents.py +++ b/autogpt_platform/backend/backend/server/v2/library/routes/agents.py @@ -1,13 +1,19 @@ import logging -from typing import Optional +from typing import Any, Optional import autogpt_libs.auth as autogpt_auth_lib -from fastapi import APIRouter, Body, Depends, HTTPException, Query, status -from fastapi.responses import JSONResponse +from fastapi import APIRouter, Body, Depends, HTTPException, Path, Query, status +from fastapi.responses import Response +from pydantic import BaseModel, Field import backend.server.v2.library.db as library_db import backend.server.v2.library.model as library_model import backend.server.v2.store.exceptions as store_exceptions +from backend.data.graph import get_graph +from backend.data.model import CredentialsMetaInput +from backend.executor.utils import make_node_credentials_input_map +from backend.integrations.webhooks.utils import setup_webhook_for_block +from backend.util.exceptions import NotFoundError logger = logging.getLogger(__name__) @@ -71,10 +77,10 @@ async def list_library_agents( page_size=page_size, ) except Exception as e: - logger.exception("Listing library agents failed for user %s: %s", user_id, e) + logger.error(f"Could not list library agents for user #{user_id}: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail={"message": str(e), "hint": "Inspect database connectivity."}, + detail=str(e), ) from e @@ -86,6 +92,23 @@ async def get_library_agent( return await library_db.get_library_agent(id=library_agent_id, user_id=user_id) +@router.get("/by-graph/{graph_id}") +async def get_library_agent_by_graph_id( + graph_id: str, + version: Optional[int] = Query(default=None), + user_id: str = Depends(autogpt_auth_lib.depends.get_user_id), +) -> library_model.LibraryAgent: + library_agent = await library_db.get_library_agent_by_graph_id( + user_id, graph_id, version + ) + if not library_agent: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Library agent for graph #{graph_id} and user #{user_id} not found", + ) + return library_agent + + @router.get( "/marketplace/{store_listing_version_id}", summary="Get Agent By Store ID", @@ -103,18 +126,16 @@ async def get_library_agent_by_store_listing_version_id( return await library_db.get_library_agent_by_store_version_id( store_listing_version_id, user_id ) - except Exception as e: - logger.exception( - "Retrieving library agent by store version failed for user %s: %s", - user_id, - e, + except NotFoundError as e: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=str(e), ) + except Exception as e: + logger.error(f"Could not fetch library agent from store version ID: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail={ - "message": str(e), - "hint": "Check if the store listing ID is valid.", - }, + detail=str(e), ) from e @@ -152,26 +173,20 @@ async def add_marketplace_agent_to_library( user_id=user_id, ) - except store_exceptions.AgentNotFoundError: + except store_exceptions.AgentNotFoundError as e: logger.warning( - "Store listing version %s not found when adding to library", - store_listing_version_id, - ) - raise HTTPException( - status_code=404, - detail={ - "message": f"Store listing version {store_listing_version_id} not found", - "hint": "Confirm the ID provided.", - }, + f"Could not find store listing version {store_listing_version_id} " + "to add to library" ) + raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=str(e)) except store_exceptions.DatabaseError as e: - logger.exception("Database error whilst adding agent to library: %s", e) + logger.error(f"Database error while adding agent to library: {e}", e) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail={"message": str(e), "hint": "Inspect DB logs for details."}, ) from e except Exception as e: - logger.exception("Unexpected error while adding agent to library: %s", e) + logger.error(f"Unexpected error while adding agent to library: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail={ @@ -181,12 +196,11 @@ async def add_marketplace_agent_to_library( ) from e -@router.put( +@router.patch( "/{library_agent_id}", summary="Update Library Agent", - status_code=status.HTTP_204_NO_CONTENT, responses={ - 204: {"description": "Agent updated successfully"}, + 200: {"description": "Agent updated successfully"}, 500: {"description": "Server error"}, }, ) @@ -194,7 +208,7 @@ async def update_library_agent( library_agent_id: str, payload: library_model.LibraryAgentUpdateRequest, user_id: str = Depends(autogpt_auth_lib.depends.get_user_id), -) -> JSONResponse: +) -> library_model.LibraryAgent: """ Update the library agent with the given fields. @@ -203,39 +217,75 @@ async def update_library_agent( payload: Fields to update (auto_update_version, is_favorite, etc.). user_id: ID of the authenticated user. - Returns: - 204 (No Content) on success. - Raises: HTTPException(500): If a server/database error occurs. """ try: - await library_db.update_library_agent( + return await library_db.update_library_agent( library_agent_id=library_agent_id, user_id=user_id, auto_update_version=payload.auto_update_version, is_favorite=payload.is_favorite, is_archived=payload.is_archived, - is_deleted=payload.is_deleted, - ) - return JSONResponse( - status_code=status.HTTP_204_NO_CONTENT, - content={"message": "Agent updated successfully"}, ) + except NotFoundError as e: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=str(e), + ) from e except store_exceptions.DatabaseError as e: - logger.exception("Database error while updating library agent: %s", e) + logger.error(f"Database error while updating library agent: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail={"message": str(e), "hint": "Verify DB connection."}, ) from e except Exception as e: - logger.exception("Unexpected error while updating library agent: %s", e) + logger.error(f"Unexpected error while updating library agent: {e}") raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail={"message": str(e), "hint": "Check server logs."}, ) from e +@router.delete( + "/{library_agent_id}", + summary="Delete Library Agent", + responses={ + 204: {"description": "Agent deleted successfully"}, + 404: {"description": "Agent not found"}, + 500: {"description": "Server error"}, + }, +) +async def delete_library_agent( + library_agent_id: str, + user_id: str = Depends(autogpt_auth_lib.depends.get_user_id), +) -> Response: + """ + Soft-delete the specified library agent. + + Args: + library_agent_id: ID of the library agent to delete. + user_id: ID of the authenticated user. + + Returns: + 204 No Content if successful. + + Raises: + HTTPException(404): If the agent does not exist. + HTTPException(500): If a server/database error occurs. + """ + try: + await library_db.delete_library_agent( + library_agent_id=library_agent_id, user_id=user_id + ) + return Response(status_code=status.HTTP_204_NO_CONTENT) + except NotFoundError as e: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=str(e), + ) from e + + @router.post("/{library_agent_id}/fork", summary="Fork Library Agent") async def fork_library_agent( library_agent_id: str, @@ -245,3 +295,81 @@ async def fork_library_agent( library_agent_id=library_agent_id, user_id=user_id, ) + + +class TriggeredPresetSetupParams(BaseModel): + name: str + description: str = "" + + trigger_config: dict[str, Any] + agent_credentials: dict[str, CredentialsMetaInput] = Field(default_factory=dict) + + +@router.post("/{library_agent_id}/setup-trigger") +async def setup_trigger( + library_agent_id: str = Path(..., description="ID of the library agent"), + params: TriggeredPresetSetupParams = Body(), + user_id: str = Depends(autogpt_auth_lib.depends.get_user_id), +) -> library_model.LibraryAgentPreset: + """ + Sets up a webhook-triggered `LibraryAgentPreset` for a `LibraryAgent`. + Returns the correspondingly created `LibraryAgentPreset` with `webhook_id` set. + """ + library_agent = await library_db.get_library_agent( + id=library_agent_id, user_id=user_id + ) + if not library_agent: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Library agent #{library_agent_id} not found", + ) + + graph = await get_graph( + library_agent.graph_id, version=library_agent.graph_version, user_id=user_id + ) + if not graph: + raise HTTPException( + status.HTTP_410_GONE, + f"Graph #{library_agent.graph_id} not accessible (anymore)", + ) + if not (trigger_node := graph.webhook_input_node): + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"Graph #{library_agent.graph_id} does not have a webhook node", + ) + + trigger_config_with_credentials = { + **params.trigger_config, + **( + make_node_credentials_input_map(graph, params.agent_credentials).get( + trigger_node.id + ) + or {} + ), + } + + new_webhook, feedback = await setup_webhook_for_block( + user_id=user_id, + trigger_block=trigger_node.block, + trigger_config=trigger_config_with_credentials, + ) + if not new_webhook: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"Could not set up webhook: {feedback}", + ) + + new_preset = await library_db.create_preset( + user_id=user_id, + preset=library_model.LibraryAgentPresetCreatable( + graph_id=library_agent.graph_id, + graph_version=library_agent.graph_version, + name=params.name, + description=params.description, + inputs=trigger_config_with_credentials, + credentials=params.agent_credentials, + webhook_id=new_webhook.id, + is_active=True, + ), + ) + return new_preset diff --git a/autogpt_platform/backend/backend/server/v2/library/routes/presets.py b/autogpt_platform/backend/backend/server/v2/library/routes/presets.py index 23fc80d23e..35bbb9f591 100644 --- a/autogpt_platform/backend/backend/server/v2/library/routes/presets.py +++ b/autogpt_platform/backend/backend/server/v2/library/routes/presets.py @@ -1,19 +1,23 @@ import logging -from typing import Annotated, Any, Optional +from typing import Any, Optional import autogpt_libs.auth as autogpt_auth_lib from fastapi import APIRouter, Body, Depends, HTTPException, Query, status import backend.server.v2.library.db as db import backend.server.v2.library.model as models -from backend.executor.utils import add_graph_execution +from backend.data.graph import get_graph +from backend.data.integrations import get_webhook +from backend.executor.utils import add_graph_execution, make_node_credentials_input_map +from backend.integrations.creds_manager import IntegrationCredentialsManager +from backend.integrations.webhooks import get_webhook_manager +from backend.integrations.webhooks.utils import setup_webhook_for_block from backend.util.exceptions import NotFoundError logger = logging.getLogger(__name__) -router = APIRouter( - tags=["presets"], -) +credentials_manager = IntegrationCredentialsManager() +router = APIRouter(tags=["presets"]) @router.get( @@ -51,11 +55,7 @@ async def list_presets( except Exception as e: logger.exception("Failed to list presets for user %s: %s", user_id, e) raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail={ - "message": str(e), - "hint": "Ensure the presets DB table is accessible.", - }, + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) ) @@ -83,21 +83,21 @@ async def get_preset( """ try: preset = await db.get_preset(user_id, preset_id) - if not preset: - raise HTTPException( - status_code=status.HTTP_404_NOT_FOUND, - detail=f"Preset {preset_id} not found", - ) - return preset except Exception as e: logger.exception( "Error retrieving preset %s for user %s: %s", preset_id, user_id, e ) raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail={"message": str(e), "hint": "Validate preset ID and retry."}, + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) ) + if not preset: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Preset #{preset_id} not found", + ) + return preset + @router.post( "/presets", @@ -134,8 +134,7 @@ async def create_preset( except Exception as e: logger.exception("Preset creation failed for user %s: %s", user_id, e) raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail={"message": str(e), "hint": "Check preset payload format."}, + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) ) @@ -163,17 +162,85 @@ async def update_preset( Raises: HTTPException: If an error occurs while updating the preset. """ + current = await get_preset(preset_id, user_id=user_id) + if not current: + raise HTTPException(status.HTTP_404_NOT_FOUND, f"Preset #{preset_id} not found") + + graph = await get_graph( + current.graph_id, + current.graph_version, + user_id=user_id, + ) + if not graph: + raise HTTPException( + status.HTTP_410_GONE, + f"Graph #{current.graph_id} not accessible (anymore)", + ) + + trigger_inputs_updated, new_webhook, feedback = False, None, None + if (trigger_node := graph.webhook_input_node) and ( + preset.inputs is not None and preset.credentials is not None + ): + trigger_config_with_credentials = { + **preset.inputs, + **( + make_node_credentials_input_map(graph, preset.credentials).get( + trigger_node.id + ) + or {} + ), + } + new_webhook, feedback = await setup_webhook_for_block( + user_id=user_id, + trigger_block=graph.webhook_input_node.block, + trigger_config=trigger_config_with_credentials, + for_preset_id=preset_id, + ) + trigger_inputs_updated = True + if not new_webhook: + raise HTTPException( + status_code=status.HTTP_400_BAD_REQUEST, + detail=f"Could not update trigger configuration: {feedback}", + ) + try: - return await db.update_preset( - user_id=user_id, preset_id=preset_id, preset=preset + updated = await db.update_preset( + user_id=user_id, + preset_id=preset_id, + inputs=preset.inputs, + credentials=preset.credentials, + name=preset.name, + description=preset.description, + is_active=preset.is_active, ) except Exception as e: logger.exception("Preset update failed for user %s: %s", user_id, e) raise HTTPException( - status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail={"message": str(e), "hint": "Check preset data and try again."}, + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=str(e) ) + # Update the webhook as well, if necessary + if trigger_inputs_updated: + updated = await db.set_preset_webhook( + user_id, preset_id, new_webhook.id if new_webhook else None + ) + + # Clean up webhook if it is now unused + if current.webhook_id and ( + current.webhook_id != (new_webhook.id if new_webhook else None) + ): + current_webhook = await get_webhook(current.webhook_id) + credentials = ( + await credentials_manager.get(user_id, current_webhook.credentials_id) + if current_webhook.credentials_id + else None + ) + await get_webhook_manager( + current_webhook.provider + ).prune_webhook_if_dangling(user_id, current_webhook.id, credentials) + + return updated + @router.delete( "/presets/{preset_id}", @@ -195,6 +262,28 @@ async def delete_preset( Raises: HTTPException: If an error occurs while deleting the preset. """ + preset = await db.get_preset(user_id, preset_id) + if not preset: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail=f"Preset #{preset_id} not found for user #{user_id}", + ) + + # Detach and clean up the attached webhook, if any + if preset.webhook_id: + webhook = await get_webhook(preset.webhook_id) + await db.set_preset_webhook(user_id, preset_id, None) + + # Clean up webhook if it is now unused + credentials = ( + await credentials_manager.get(user_id, webhook.credentials_id) + if webhook.credentials_id + else None + ) + await get_webhook_manager(webhook.provider).prune_webhook_if_dangling( + user_id, webhook.id, credentials + ) + try: await db.delete_preset(user_id, preset_id) except Exception as e: @@ -203,7 +292,7 @@ async def delete_preset( ) raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, - detail={"message": str(e), "hint": "Ensure preset exists before deleting."}, + detail=str(e), ) @@ -214,24 +303,20 @@ async def delete_preset( description="Execute a preset with the given graph and node input for the current user.", ) async def execute_preset( - graph_id: str, - graph_version: int, preset_id: str, - node_input: Annotated[dict[str, Any], Body(..., embed=True, default_factory=dict)], user_id: str = Depends(autogpt_auth_lib.depends.get_user_id), + inputs: dict[str, Any] = Body(..., embed=True, default_factory=dict), ) -> dict[str, Any]: # FIXME: add proper return type """ Execute a preset given graph parameters, returning the execution ID on success. Args: - graph_id (str): ID of the graph to execute. - graph_version (int): Version of the graph to execute. preset_id (str): ID of the preset to execute. - node_input (Dict[Any, Any]): Input data for the node. user_id (str): ID of the authenticated user. + inputs (dict[str, Any]): Optionally, additional input data for the graph execution. Returns: - Dict[str, Any]: A response containing the execution ID. + {id: graph_exec_id}: A response containing the execution ID. Raises: HTTPException: If the preset is not found or an error occurs while executing the preset. @@ -241,18 +326,18 @@ async def execute_preset( if not preset: raise HTTPException( status_code=status.HTTP_404_NOT_FOUND, - detail="Preset not found", + detail=f"Preset #{preset_id} not found", ) # Merge input overrides with preset inputs - merged_node_input = preset.inputs | node_input + merged_node_input = preset.inputs | inputs execution = await add_graph_execution( - graph_id=graph_id, user_id=user_id, - inputs=merged_node_input, + graph_id=preset.graph_id, + graph_version=preset.graph_version, preset_id=preset_id, - graph_version=graph_version, + inputs=merged_node_input, ) logger.debug(f"Execution added: {execution} with input: {merged_node_input}") @@ -263,9 +348,6 @@ async def execute_preset( except Exception as e: logger.exception("Preset execution failed for user %s: %s", user_id, e) raise HTTPException( - status_code=status.HTTP_400_BAD_REQUEST, - detail={ - "message": str(e), - "hint": "Review preset configuration and graph ID.", - }, + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=str(e), ) diff --git a/autogpt_platform/backend/backend/server/v2/library/routes_test.py b/autogpt_platform/backend/backend/server/v2/library/routes_test.py index 6cfb1ffce0..97c20e9d92 100644 --- a/autogpt_platform/backend/backend/server/v2/library/routes_test.py +++ b/autogpt_platform/backend/backend/server/v2/library/routes_test.py @@ -50,6 +50,8 @@ async def test_get_library_agents_success( creator_name="Test Creator", creator_image_url="", input_schema={"type": "object", "properties": {}}, + credentials_input_schema={"type": "object", "properties": {}}, + has_external_trigger=False, status=library_model.LibraryAgentStatus.COMPLETED, new_output=False, can_access_graph=True, @@ -66,6 +68,8 @@ async def test_get_library_agents_success( creator_name="Test Creator", creator_image_url="", input_schema={"type": "object", "properties": {}}, + credentials_input_schema={"type": "object", "properties": {}}, + has_external_trigger=False, status=library_model.LibraryAgentStatus.COMPLETED, new_output=False, can_access_graph=False, @@ -117,26 +121,57 @@ def test_get_library_agents_error(mocker: pytest_mock.MockFixture): ) -@pytest.mark.skip(reason="Mocker Not implemented") def test_add_agent_to_library_success(mocker: pytest_mock.MockFixture): - mock_db_call = mocker.patch("backend.server.v2.library.db.add_agent_to_library") - mock_db_call.return_value = None + mock_library_agent = library_model.LibraryAgent( + id="test-library-agent-id", + graph_id="test-agent-1", + graph_version=1, + name="Test Agent 1", + description="Test Description 1", + image_url=None, + creator_name="Test Creator", + creator_image_url="", + input_schema={"type": "object", "properties": {}}, + credentials_input_schema={"type": "object", "properties": {}}, + has_external_trigger=False, + status=library_model.LibraryAgentStatus.COMPLETED, + new_output=False, + can_access_graph=True, + is_latest_version=True, + updated_at=FIXED_NOW, + ) - response = client.post("/agents/test-version-id") + mock_db_call = mocker.patch( + "backend.server.v2.library.db.add_store_agent_to_library" + ) + mock_db_call.return_value = mock_library_agent + + response = client.post( + "/agents", json={"store_listing_version_id": "test-version-id"} + ) assert response.status_code == 201 + + # Verify the response contains the library agent data + data = library_model.LibraryAgent.model_validate(response.json()) + assert data.id == "test-library-agent-id" + assert data.graph_id == "test-agent-1" + mock_db_call.assert_called_once_with( store_listing_version_id="test-version-id", user_id="test-user-id" ) -@pytest.mark.skip(reason="Mocker Not implemented") def test_add_agent_to_library_error(mocker: pytest_mock.MockFixture): - mock_db_call = mocker.patch("backend.server.v2.library.db.add_agent_to_library") + mock_db_call = mocker.patch( + "backend.server.v2.library.db.add_store_agent_to_library" + ) mock_db_call.side_effect = Exception("Test error") - response = client.post("/agents/test-version-id") + response = client.post( + "/agents", json={"store_listing_version_id": "test-version-id"} + ) assert response.status_code == 500 - assert response.json()["detail"] == "Failed to add agent to library" + assert "detail" in response.json() # Verify error response structure mock_db_call.assert_called_once_with( store_listing_version_id="test-version-id", user_id="test-user-id" ) diff --git a/autogpt_platform/backend/backend/server/v2/otto/routes_test.py b/autogpt_platform/backend/backend/server/v2/otto/routes_test.py index 7bc5f25ab0..861ee7a4f6 100644 --- a/autogpt_platform/backend/backend/server/v2/otto/routes_test.py +++ b/autogpt_platform/backend/backend/server/v2/otto/routes_test.py @@ -259,8 +259,8 @@ def test_ask_otto_unauthenticated(mocker: pytest_mock.MockFixture) -> None: } response = client.post("/ask", json=request_data) - # When auth is disabled and Otto API URL is not configured, we get 503 - assert response.status_code == 503 + # When auth is disabled and Otto API URL is not configured, we get 502 (wrapped from 503) + assert response.status_code == 502 # Restore the override app.dependency_overrides[autogpt_libs.auth.middleware.auth_middleware] = ( diff --git a/autogpt_platform/backend/backend/server/v2/store/db_test.py b/autogpt_platform/backend/backend/server/v2/store/db_test.py index 14c3f747fd..7ad4509c19 100644 --- a/autogpt_platform/backend/backend/server/v2/store/db_test.py +++ b/autogpt_platform/backend/backend/server/v2/store/db_test.py @@ -93,6 +93,14 @@ async def test_get_store_agent_details(mocker): mock_store_agent = mocker.patch("prisma.models.StoreAgent.prisma") mock_store_agent.return_value.find_first = mocker.AsyncMock(return_value=mock_agent) + # Mock Profile prisma call + mock_profile = mocker.MagicMock() + mock_profile.userId = "user-id-123" + mock_profile_db = mocker.patch("prisma.models.Profile.prisma") + mock_profile_db.return_value.find_first = mocker.AsyncMock( + return_value=mock_profile + ) + # Mock StoreListing prisma call - this is what was missing mock_store_listing_db = mocker.patch("prisma.models.StoreListing.prisma") mock_store_listing_db.return_value.find_first = mocker.AsyncMock( diff --git a/autogpt_platform/backend/backend/server/v2/store/exceptions.py b/autogpt_platform/backend/backend/server/v2/store/exceptions.py index 3823974121..d92196527f 100644 --- a/autogpt_platform/backend/backend/server/v2/store/exceptions.py +++ b/autogpt_platform/backend/backend/server/v2/store/exceptions.py @@ -34,6 +34,20 @@ class StorageUploadError(MediaUploadError): pass +class VirusDetectedError(MediaUploadError): + """Raised when a virus is detected in uploaded file""" + + def __init__(self, threat_name: str, message: str | None = None): + self.threat_name = threat_name + super().__init__(message or f"Virus detected: {threat_name}") + + +class VirusScanError(MediaUploadError): + """Raised when virus scanning fails""" + + pass + + class StoreError(Exception): """Base exception for store-related errors""" diff --git a/autogpt_platform/backend/backend/server/v2/store/media.py b/autogpt_platform/backend/backend/server/v2/store/media.py index 91f4da03c1..ddcd5864d2 100644 --- a/autogpt_platform/backend/backend/server/v2/store/media.py +++ b/autogpt_platform/backend/backend/server/v2/store/media.py @@ -8,6 +8,7 @@ from google.cloud import storage import backend.server.v2.store.exceptions from backend.util.exceptions import MissingConfigError from backend.util.settings import Settings +from backend.util.virus_scanner import scan_content_safe logger = logging.getLogger(__name__) @@ -67,7 +68,7 @@ async def upload_media( # Validate file signature/magic bytes if file.content_type in ALLOWED_IMAGE_TYPES: # Check image file signatures - if content.startswith(b"\xFF\xD8\xFF"): # JPEG + if content.startswith(b"\xff\xd8\xff"): # JPEG if file.content_type != "image/jpeg": raise backend.server.v2.store.exceptions.InvalidFileTypeError( "File signature does not match content type" @@ -175,6 +176,7 @@ async def upload_media( blob.content_type = content_type file_bytes = await file.read() + await scan_content_safe(file_bytes, filename=unique_filename) blob.upload_from_string(file_bytes, content_type=content_type) public_url = blob.public_url diff --git a/autogpt_platform/backend/backend/server/v2/store/routes.py b/autogpt_platform/backend/backend/server/v2/store/routes.py index 96218d52a8..6cc2721968 100644 --- a/autogpt_platform/backend/backend/server/v2/store/routes.py +++ b/autogpt_platform/backend/backend/server/v2/store/routes.py @@ -12,6 +12,7 @@ from autogpt_libs.auth.depends import auth_middleware, get_user_id import backend.data.block import backend.data.graph import backend.server.v2.store.db +import backend.server.v2.store.exceptions import backend.server.v2.store.image_gen import backend.server.v2.store.media import backend.server.v2.store.model @@ -589,6 +590,25 @@ async def upload_submission_media( user_id=user_id, file=file ) return media_url + except backend.server.v2.store.exceptions.VirusDetectedError as e: + logger.warning(f"Virus detected in uploaded file: {e.threat_name}") + return fastapi.responses.JSONResponse( + status_code=400, + content={ + "detail": f"File rejected due to virus detection: {e.threat_name}", + "error_type": "virus_detected", + "threat_name": e.threat_name, + }, + ) + except backend.server.v2.store.exceptions.VirusScanError as e: + logger.error(f"Virus scanning failed: {str(e)}") + return fastapi.responses.JSONResponse( + status_code=503, + content={ + "detail": "Virus scanning service unavailable. Please try again later.", + "error_type": "virus_scan_failed", + }, + ) except Exception: logger.exception("Exception occurred whilst uploading submission media") return fastapi.responses.JSONResponse( diff --git a/autogpt_platform/backend/test/server/test_ws_api.py b/autogpt_platform/backend/backend/server/ws_api_test.py similarity index 90% rename from autogpt_platform/backend/test/server/test_ws_api.py rename to autogpt_platform/backend/backend/server/ws_api_test.py index 0eb52485e8..c9c27eb086 100644 --- a/autogpt_platform/backend/test/server/test_ws_api.py +++ b/autogpt_platform/backend/backend/server/ws_api_test.py @@ -19,7 +19,9 @@ from backend.server.ws_api import ( @pytest.fixture def mock_websocket() -> AsyncMock: - return AsyncMock(spec=WebSocket) + mock = AsyncMock(spec=WebSocket) + mock.query_params = {} # Add query_params attribute for authentication + return mock @pytest.fixture @@ -29,8 +31,13 @@ def mock_manager() -> AsyncMock: @pytest.mark.asyncio async def test_websocket_router_subscribe( - mock_websocket: AsyncMock, mock_manager: AsyncMock, snapshot: Snapshot + mock_websocket: AsyncMock, mock_manager: AsyncMock, snapshot: Snapshot, mocker ) -> None: + # Mock the authenticate_websocket function to ensure it returns a valid user_id + mocker.patch( + "backend.server.ws_api.authenticate_websocket", return_value=DEFAULT_USER_ID + ) + mock_websocket.receive_text.side_effect = [ WSMessage( method=WSMethod.SUBSCRIBE_GRAPH_EXEC, @@ -70,8 +77,13 @@ async def test_websocket_router_subscribe( @pytest.mark.asyncio async def test_websocket_router_unsubscribe( - mock_websocket: AsyncMock, mock_manager: AsyncMock, snapshot: Snapshot + mock_websocket: AsyncMock, mock_manager: AsyncMock, snapshot: Snapshot, mocker ) -> None: + # Mock the authenticate_websocket function to ensure it returns a valid user_id + mocker.patch( + "backend.server.ws_api.authenticate_websocket", return_value=DEFAULT_USER_ID + ) + mock_websocket.receive_text.side_effect = [ WSMessage( method=WSMethod.UNSUBSCRIBE, @@ -108,8 +120,13 @@ async def test_websocket_router_unsubscribe( @pytest.mark.asyncio async def test_websocket_router_invalid_method( - mock_websocket: AsyncMock, mock_manager: AsyncMock + mock_websocket: AsyncMock, mock_manager: AsyncMock, mocker ) -> None: + # Mock the authenticate_websocket function to ensure it returns a valid user_id + mocker.patch( + "backend.server.ws_api.authenticate_websocket", return_value=DEFAULT_USER_ID + ) + mock_websocket.receive_text.side_effect = [ WSMessage(method=WSMethod.GRAPH_EXECUTION_EVENT).model_dump_json(), WebSocketDisconnect(), diff --git a/autogpt_platform/backend/backend/util/decorator.py b/autogpt_platform/backend/backend/util/decorator.py index 84f128333f..a0b0ce91ba 100644 --- a/autogpt_platform/backend/backend/util/decorator.py +++ b/autogpt_platform/backend/backend/util/decorator.py @@ -2,7 +2,17 @@ import functools import logging import os import time -from typing import Any, Awaitable, Callable, Coroutine, ParamSpec, Tuple, TypeVar +from typing import ( + Any, + Awaitable, + Callable, + Coroutine, + Literal, + ParamSpec, + Tuple, + TypeVar, + overload, +) from pydantic import BaseModel @@ -72,37 +82,115 @@ def async_time_measured( return async_wrapper -def error_logged(func: Callable[P, T]) -> Callable[P, T | None]: +@overload +def error_logged( + *, swallow: Literal[True] +) -> Callable[[Callable[P, T]], Callable[P, T | None]]: ... + + +@overload +def error_logged( + *, swallow: Literal[False] +) -> Callable[[Callable[P, T]], Callable[P, T]]: ... + + +@overload +def error_logged() -> Callable[[Callable[P, T]], Callable[P, T | None]]: ... + + +def error_logged( + *, swallow: bool = True +) -> ( + Callable[[Callable[P, T]], Callable[P, T | None]] + | Callable[[Callable[P, T]], Callable[P, T]] +): """ - Decorator to suppress and log any exceptions raised by a function. + Decorator to log any exceptions raised by a function, with optional suppression. + + Args: + swallow: Whether to suppress the exception (True) or re-raise it (False). Default is True. + + Usage: + @error_logged() # Default behavior (swallow errors) + @error_logged(swallow=False) # Log and re-raise errors """ - @functools.wraps(func) - def wrapper(*args: P.args, **kwargs: P.kwargs) -> T | None: - try: - return func(*args, **kwargs) - except Exception as e: - logger.exception( - f"Error when calling function {func.__name__} with arguments {args} {kwargs}: {e}" - ) + def decorator(f: Callable[P, T]) -> Callable[P, T | None]: + @functools.wraps(f) + def wrapper(*args: P.args, **kwargs: P.kwargs) -> T | None: + try: + return f(*args, **kwargs) + except Exception as e: + logger.exception( + f"Error when calling function {f.__name__} with arguments {args} {kwargs}: {e}" + ) + if not swallow: + raise + return None - return wrapper + return wrapper + + return decorator +@overload def async_error_logged( - func: Callable[P, Coroutine[Any, Any, T]], -) -> Callable[P, Coroutine[Any, Any, T | None]]: + *, swallow: Literal[True] +) -> Callable[ + [Callable[P, Coroutine[Any, Any, T]]], Callable[P, Coroutine[Any, Any, T | None]] +]: ... + + +@overload +def async_error_logged( + *, swallow: Literal[False] +) -> Callable[ + [Callable[P, Coroutine[Any, Any, T]]], Callable[P, Coroutine[Any, Any, T]] +]: ... + + +@overload +def async_error_logged() -> Callable[ + [Callable[P, Coroutine[Any, Any, T]]], + Callable[P, Coroutine[Any, Any, T | None]], +]: ... + + +def async_error_logged(*, swallow: bool = True) -> ( + Callable[ + [Callable[P, Coroutine[Any, Any, T]]], + Callable[P, Coroutine[Any, Any, T | None]], + ] + | Callable[ + [Callable[P, Coroutine[Any, Any, T]]], Callable[P, Coroutine[Any, Any, T]] + ] +): """ - Decorator to suppress and log any exceptions raised by an async function. + Decorator to log any exceptions raised by an async function, with optional suppression. + + Args: + swallow: Whether to suppress the exception (True) or re-raise it (False). Default is True. + + Usage: + @async_error_logged() # Default behavior (swallow errors) + @async_error_logged(swallow=False) # Log and re-raise errors """ - @functools.wraps(func) - async def wrapper(*args: P.args, **kwargs: P.kwargs) -> T | None: - try: - return await func(*args, **kwargs) - except Exception as e: - logger.exception( - f"Error when calling async function {func.__name__} with arguments {args} {kwargs}: {e}" - ) + def decorator( + f: Callable[P, Coroutine[Any, Any, T]] + ) -> Callable[P, Coroutine[Any, Any, T | None]]: + @functools.wraps(f) + async def wrapper(*args: P.args, **kwargs: P.kwargs) -> T | None: + try: + return await f(*args, **kwargs) + except Exception as e: + logger.exception( + f"Error when calling async function {f.__name__} with arguments {args} {kwargs}: {e}" + ) + if not swallow: + raise + return None - return wrapper + return wrapper + + return decorator diff --git a/autogpt_platform/backend/backend/util/decorator_test.py b/autogpt_platform/backend/backend/util/decorator_test.py new file mode 100644 index 0000000000..cdc1cc5b75 --- /dev/null +++ b/autogpt_platform/backend/backend/util/decorator_test.py @@ -0,0 +1,74 @@ +import time + +import pytest + +from backend.util.decorator import async_error_logged, error_logged, time_measured + + +@time_measured +def example_function(a: int, b: int, c: int) -> int: + time.sleep(0.5) + return a + b + c + + +@error_logged(swallow=True) +def example_function_with_error_swallowed(a: int, b: int, c: int) -> int: + raise ValueError("This error should be swallowed") + + +@error_logged(swallow=False) +def example_function_with_error_not_swallowed(a: int, b: int, c: int) -> int: + raise ValueError("This error should NOT be swallowed") + + +@async_error_logged(swallow=True) +async def async_function_with_error_swallowed() -> int: + raise ValueError("This async error should be swallowed") + + +@async_error_logged(swallow=False) +async def async_function_with_error_not_swallowed() -> int: + raise ValueError("This async error should NOT be swallowed") + + +def test_timer_decorator(): + """Test that the time_measured decorator correctly measures execution time.""" + info, res = example_function(1, 2, 3) + assert info.cpu_time >= 0 + assert info.wall_time >= 0.4 + assert res == 6 + + +def test_error_decorator_swallow_true(): + """Test that error_logged(swallow=True) logs and swallows errors.""" + res = example_function_with_error_swallowed(1, 2, 3) + assert res is None + + +def test_error_decorator_swallow_false(): + """Test that error_logged(swallow=False) logs errors but re-raises them.""" + with pytest.raises(ValueError, match="This error should NOT be swallowed"): + example_function_with_error_not_swallowed(1, 2, 3) + + +def test_async_error_decorator_swallow_true(): + """Test that async_error_logged(swallow=True) logs and swallows errors.""" + import asyncio + + async def run_test(): + res = await async_function_with_error_swallowed() + return res + + res = asyncio.run(run_test()) + assert res is None + + +def test_async_error_decorator_swallow_false(): + """Test that async_error_logged(swallow=False) logs errors but re-raises them.""" + import asyncio + + async def run_test(): + await async_function_with_error_not_swallowed() + + with pytest.raises(ValueError, match="This async error should NOT be swallowed"): + asyncio.run(run_test()) diff --git a/autogpt_platform/backend/backend/util/exceptions.py b/autogpt_platform/backend/backend/util/exceptions.py index 0d0fa0cf96..8f2eacc4ea 100644 --- a/autogpt_platform/backend/backend/util/exceptions.py +++ b/autogpt_platform/backend/backend/util/exceptions.py @@ -10,6 +10,10 @@ class NeedConfirmation(Exception): """The user must explicitly confirm that they want to proceed""" +class NotAuthorizedError(ValueError): + """The user is not authorized to perform the requested operation""" + + class InsufficientBalanceError(ValueError): user_id: str message: str diff --git a/autogpt_platform/backend/backend/util/file.py b/autogpt_platform/backend/backend/util/file.py index 27ad4cdd19..bd7c4d3737 100644 --- a/autogpt_platform/backend/backend/util/file.py +++ b/autogpt_platform/backend/backend/util/file.py @@ -9,6 +9,7 @@ from urllib.parse import urlparse from backend.util.request import Requests from backend.util.type import MediaFileType +from backend.util.virus_scanner import scan_content_safe TEMP_DIR = Path(tempfile.gettempdir()).resolve() @@ -105,7 +106,11 @@ async def store_media_file( extension = _extension_from_mime(mime_type) filename = f"{uuid.uuid4()}{extension}" target_path = _ensure_inside_base(base_path / filename, base_path) - target_path.write_bytes(base64.b64decode(b64_content)) + content = base64.b64decode(b64_content) + + # Virus scan the base64 content before writing + await scan_content_safe(content, filename=filename) + target_path.write_bytes(content) elif file.startswith(("http://", "https://")): # URL @@ -115,6 +120,9 @@ async def store_media_file( # Download and save resp = await Requests().get(file) + + # Virus scan the downloaded content before writing + await scan_content_safe(resp.content, filename=filename) target_path.write_bytes(resp.content) else: diff --git a/autogpt_platform/backend/backend/util/json.py b/autogpt_platform/backend/backend/util/json.py index d71b61a246..505a7b27c8 100644 --- a/autogpt_platform/backend/backend/util/json.py +++ b/autogpt_platform/backend/backend/util/json.py @@ -14,8 +14,37 @@ def to_dict(data) -> dict: return jsonable_encoder(data) -def dumps(data) -> str: - return json.dumps(to_dict(data)) +def dumps(data: Any, *args: Any, **kwargs: Any) -> str: + """ + Serialize data to JSON string with automatic conversion of Pydantic models and complex types. + + This function converts the input data to a JSON-serializable format using FastAPI's + jsonable_encoder before dumping to JSON. It handles Pydantic models, complex types, + and ensures proper serialization. + + Parameters + ---------- + data : Any + The data to serialize. Can be any type including Pydantic models, dicts, lists, etc. + *args : Any + Additional positional arguments passed to json.dumps() + **kwargs : Any + Additional keyword arguments passed to json.dumps() (e.g., indent, separators) + + Returns + ------- + str + JSON string representation of the data + + Examples + -------- + >>> dumps({"name": "Alice", "age": 30}) + '{"name": "Alice", "age": 30}' + + >>> dumps(pydantic_model_instance, indent=2) + '{\n "field1": "value1",\n "field2": "value2"\n}' + """ + return json.dumps(to_dict(data), *args, **kwargs) T = TypeVar("T") diff --git a/autogpt_platform/backend/backend/util/logging.py b/autogpt_platform/backend/backend/util/logging.py index 9b6a64deac..5f822c94a7 100644 --- a/autogpt_platform/backend/backend/util/logging.py +++ b/autogpt_platform/backend/backend/util/logging.py @@ -1,4 +1,4 @@ -from logging import Logger +import logging from backend.util.settings import AppEnvironment, BehaveAs, Settings @@ -6,8 +6,6 @@ settings = Settings() def configure_logging(): - import logging - import autogpt_libs.logging.config if ( @@ -25,7 +23,7 @@ def configure_logging(): class TruncatedLogger: def __init__( self, - logger: Logger, + logger: logging.Logger, prefix: str = "", metadata: dict | None = None, max_length: int = 1000, @@ -65,3 +63,13 @@ class TruncatedLogger: if len(text) > self.max_length: text = text[: self.max_length] + "..." return text + + +class PrefixFilter(logging.Filter): + def __init__(self, prefix: str): + super().__init__() + self.prefix = prefix + + def filter(self, record): + record.msg = f"{self.prefix} {record.msg}" + return True diff --git a/autogpt_platform/backend/backend/util/prompt.py b/autogpt_platform/backend/backend/util/prompt.py new file mode 100644 index 0000000000..b098e48d6f --- /dev/null +++ b/autogpt_platform/backend/backend/util/prompt.py @@ -0,0 +1,206 @@ +from copy import deepcopy +from typing import Any + +from tiktoken import encoding_for_model + +from backend.util import json + +# ---------------------------------------------------------------------------# +# INTERNAL UTILITIES # +# ---------------------------------------------------------------------------# + + +def _tok_len(text: str, enc) -> int: + """True token length of *text* in tokenizer *enc* (no wrapper cost).""" + return len(enc.encode(text)) + + +def _msg_tokens(msg: dict, enc) -> int: + """ + OpenAI counts ≈3 wrapper tokens per chat message, plus 1 if "name" + is present, plus the tokenised content length. + """ + WRAPPER = 3 + (1 if "name" in msg else 0) + return WRAPPER + _tok_len(msg.get("content") or "", enc) + + +def _truncate_middle_tokens(text: str, enc, max_tok: int) -> str: + """ + Return *text* shortened to ≈max_tok tokens by keeping the head & tail + and inserting an ellipsis token in the middle. + """ + ids = enc.encode(text) + if len(ids) <= max_tok: + return text # nothing to do + + # Split the allowance between the two ends: + head = max_tok // 2 - 1 # -1 for the ellipsis + tail = max_tok - head - 1 + mid = enc.encode(" … ") + return enc.decode(ids[:head] + mid + ids[-tail:]) + + +# ---------------------------------------------------------------------------# +# PUBLIC API # +# ---------------------------------------------------------------------------# + + +def compress_prompt( + messages: list[dict], + target_tokens: int, + *, + model: str = "gpt-4o", + reserve: int = 2_048, + start_cap: int = 8_192, + floor_cap: int = 128, + lossy_ok: bool = True, +) -> list[dict]: + """ + Shrink *messages* so that:: + + token_count(prompt) + reserve ≤ target_tokens + + Strategy + -------- + 1. **Token-aware truncation** – progressively halve a per-message cap + (`start_cap`, `start_cap/2`, … `floor_cap`) and apply it to the + *content* of every message except the first and last. Tool shells + are included: we keep the envelope but shorten huge payloads. + 2. **Middle-out deletion** – if still over the limit, delete whole + messages working outward from the centre, **skipping** any message + that contains ``tool_calls`` or has ``role == "tool"``. + 3. **Last-chance trim** – if still too big, truncate the *first* and + *last* message bodies down to `floor_cap` tokens. + 4. If the prompt is *still* too large: + • raise ``ValueError`` when ``lossy_ok == False`` (default) + • return the partially-trimmed prompt when ``lossy_ok == True`` + + Parameters + ---------- + messages Complete chat history (will be deep-copied). + model Model name; passed to tiktoken to pick the right + tokenizer (gpt-4o → 'o200k_base', others fallback). + target_tokens Hard ceiling for prompt size **excluding** the model's + forthcoming answer. + reserve How many tokens you want to leave available for that + answer (`max_tokens` in your subsequent completion call). + start_cap Initial per-message truncation ceiling (tokens). + floor_cap Lowest cap we'll accept before moving to deletions. + lossy_ok If *True* return best-effort prompt instead of raising + after all trim passes have been exhausted. + + Returns + ------- + list[dict] – A *new* messages list that abides by the rules above. + """ + enc = encoding_for_model(model) # best-match tokenizer + msgs = deepcopy(messages) # never mutate caller + + def total_tokens() -> int: + """Current size of *msgs* in tokens.""" + return sum(_msg_tokens(m, enc) for m in msgs) + + original_token_count = total_tokens() + if original_token_count + reserve <= target_tokens: + return msgs + + # ---- STEP 0 : normalise content -------------------------------------- + # Convert non-string payloads to strings so token counting is coherent. + for m in msgs[1:-1]: # keep the first & last intact + if not isinstance(m.get("content"), str) and m.get("content") is not None: + # Reasonable 20k-char ceiling prevents pathological blobs + content_str = json.dumps(m["content"], separators=(",", ":")) + if len(content_str) > 20_000: + content_str = _truncate_middle_tokens(content_str, enc, 20_000) + m["content"] = content_str + + # ---- STEP 1 : token-aware truncation --------------------------------- + cap = start_cap + while total_tokens() + reserve > target_tokens and cap >= floor_cap: + for m in msgs[1:-1]: # keep first & last intact + if _tok_len(m.get("content") or "", enc) > cap: + m["content"] = _truncate_middle_tokens(m["content"], enc, cap) + cap //= 2 # tighten the screw + + # ---- STEP 2 : middle-out deletion ----------------------------------- + while total_tokens() + reserve > target_tokens and len(msgs) > 2: + centre = len(msgs) // 2 + # Build a symmetrical centre-out index walk: centre, centre+1, centre-1, ... + order = [centre] + [ + i + for pair in zip(range(centre + 1, len(msgs) - 1), range(centre - 1, 0, -1)) + for i in pair + ] + removed = False + for i in order: + msg = msgs[i] + if "tool_calls" in msg or msg.get("role") == "tool": + continue # protect tool shells + del msgs[i] + removed = True + break + if not removed: # nothing more we can drop + break + + # ---- STEP 3 : final safety-net trim on first & last ------------------ + cap = start_cap + while total_tokens() + reserve > target_tokens and cap >= floor_cap: + for idx in (0, -1): # first and last + text = msgs[idx].get("content") or "" + if _tok_len(text, enc) > cap: + msgs[idx]["content"] = _truncate_middle_tokens(text, enc, cap) + cap //= 2 # tighten the screw + + # ---- STEP 4 : success or fail-gracefully ----------------------------- + if total_tokens() + reserve > target_tokens and not lossy_ok: + raise ValueError( + "compress_prompt: prompt still exceeds budget " + f"({total_tokens() + reserve} > {target_tokens})." + ) + + return msgs + + +def estimate_token_count( + messages: list[dict], + *, + model: str = "gpt-4o", +) -> int: + """ + Return the true token count of *messages* when encoded for *model*. + + Parameters + ---------- + messages Complete chat history. + model Model name; passed to tiktoken to pick the right + tokenizer (gpt-4o → 'o200k_base', others fallback). + + Returns + ------- + int – Token count. + """ + enc = encoding_for_model(model) # best-match tokenizer + return sum(_msg_tokens(m, enc) for m in messages) + + +def estimate_token_count_str( + text: Any, + *, + model: str = "gpt-4o", +) -> int: + """ + Return the true token count of *text* when encoded for *model*. + + Parameters + ---------- + text Input text. + model Model name; passed to tiktoken to pick the right + tokenizer (gpt-4o → 'o200k_base', others fallback). + + Returns + ------- + int – Token count. + """ + enc = encoding_for_model(model) # best-match tokenizer + text = json.dumps(text) if not isinstance(text, str) else text + return _tok_len(text, enc) diff --git a/autogpt_platform/backend/backend/util/request.py b/autogpt_platform/backend/backend/util/request.py index 9a4ee81392..08ac08ec50 100644 --- a/autogpt_platform/backend/backend/util/request.py +++ b/autogpt_platform/backend/backend/util/request.py @@ -430,7 +430,13 @@ class Requests: ) as response: if self.raise_for_status: - response.raise_for_status() + try: + response.raise_for_status() + except ClientResponseError as e: + body = await response.read() + raise Exception( + f"HTTP {response.status} Error: {response.reason}, Body: {body.decode(errors='replace')}" + ) from e # If allowed and a redirect is received, follow the redirect manually if allow_redirects and response.status in (301, 302, 303, 307, 308): diff --git a/autogpt_platform/backend/test/util/test_request.py b/autogpt_platform/backend/backend/util/request_test.py similarity index 100% rename from autogpt_platform/backend/test/util/test_request.py rename to autogpt_platform/backend/backend/util/request_test.py diff --git a/autogpt_platform/backend/test/util/test_retry.py b/autogpt_platform/backend/backend/util/retry_test.py similarity index 100% rename from autogpt_platform/backend/test/util/test_retry.py rename to autogpt_platform/backend/backend/util/retry_test.py diff --git a/autogpt_platform/backend/backend/util/service.py b/autogpt_platform/backend/backend/util/service.py index abcfd75dd3..d9e3e55eb8 100644 --- a/autogpt_platform/backend/backend/util/service.py +++ b/autogpt_platform/backend/backend/util/service.py @@ -31,7 +31,7 @@ from tenacity import ( wait_exponential_jitter, ) -from backend.util.exceptions import InsufficientBalanceError +import backend.util.exceptions as exceptions from backend.util.json import to_dict from backend.util.metrics import sentry_init from backend.util.process import AppProcess, get_service_name @@ -106,7 +106,13 @@ EXCEPTION_MAPPING = { ValueError, TimeoutError, ConnectionError, - InsufficientBalanceError, + *[ + ErrorType + for _, ErrorType in inspect.getmembers(exceptions) + if inspect.isclass(ErrorType) + and issubclass(ErrorType, Exception) + and ErrorType.__module__ == exceptions.__name__ + ], ] } diff --git a/autogpt_platform/backend/test/util/test_service.py b/autogpt_platform/backend/backend/util/service_test.py similarity index 97% rename from autogpt_platform/backend/test/util/test_service.py rename to autogpt_platform/backend/backend/util/service_test.py index 49f7d6f66a..3d8eb29419 100644 --- a/autogpt_platform/backend/test/util/test_service.py +++ b/autogpt_platform/backend/backend/util/service_test.py @@ -51,7 +51,7 @@ class ServiceTestClient(AppServiceClient): subtract_async = endpoint_to_async(ServiceTest.subtract) -@pytest.mark.asyncio(loop_scope="session") +@pytest.mark.asyncio async def test_service_creation(server): with ServiceTest(): client = get_service_client(ServiceTestClient) diff --git a/autogpt_platform/backend/backend/util/settings.py b/autogpt_platform/backend/backend/util/settings.py index b2691cfd18..feef2d3c97 100644 --- a/autogpt_platform/backend/backend/util/settings.py +++ b/autogpt_platform/backend/backend/util/settings.py @@ -238,6 +238,31 @@ class Config(UpdateTrackingModel["Config"], BaseSettings): description="The Discord channel for the platform", ) + clamav_service_host: str = Field( + default="localhost", + description="The host for the ClamAV daemon", + ) + clamav_service_port: int = Field( + default=3310, + description="The port for the ClamAV daemon", + ) + clamav_service_timeout: int = Field( + default=60, + description="The timeout in seconds for the ClamAV daemon", + ) + clamav_service_enabled: bool = Field( + default=True, + description="Whether virus scanning is enabled or not", + ) + clamav_max_concurrency: int = Field( + default=10, + description="The maximum number of concurrent scans to perform", + ) + clamav_mark_failed_scans_as_clean: bool = Field( + default=False, + description="Whether to mark failed scans as clean or not", + ) + @field_validator("platform_base_url", "frontend_base_url") @classmethod def validate_platform_base_url(cls, v: str, info: ValidationInfo) -> str: diff --git a/autogpt_platform/backend/test/test_data_creator.py b/autogpt_platform/backend/backend/util/test_data_creator.py similarity index 97% rename from autogpt_platform/backend/test/test_data_creator.py rename to autogpt_platform/backend/backend/util/test_data_creator.py index a61c836caf..f3e4c45614 100644 --- a/autogpt_platform/backend/test/test_data_creator.py +++ b/autogpt_platform/backend/backend/util/test_data_creator.py @@ -152,9 +152,16 @@ async def main(): print(f"Inserting {NUM_USERS * MAX_AGENTS_PER_USER} user agents") for user in users: num_agents = random.randint(MIN_AGENTS_PER_USER, MAX_AGENTS_PER_USER) - for _ in range(num_agents): # Create 1 LibraryAgent per user - graph = random.choice(agent_graphs) - preset = random.choice(agent_presets) + + # Get a shuffled list of graphs to ensure uniqueness per user + available_graphs = agent_graphs.copy() + random.shuffle(available_graphs) + + # Limit to available unique graphs + num_agents = min(num_agents, len(available_graphs)) + + for i in range(num_agents): + graph = available_graphs[i] # Use unique graph for each library agent user_agent = await db.libraryagent.create( data={ "userId": user.id, @@ -180,7 +187,7 @@ async def main(): MIN_EXECUTIONS_PER_GRAPH, MAX_EXECUTIONS_PER_GRAPH ) for _ in range(num_executions): - matching_presets = [p for p in agent_presets if p.agentId == graph.id] + matching_presets = [p for p in agent_presets if p.agentGraphId == graph.id] preset = ( random.choice(matching_presets) if matching_presets and random.random() < 0.5 @@ -355,7 +362,7 @@ async def main(): store_listing_versions = [] print(f"Inserting {NUM_USERS} store listing versions") for listing in store_listings: - graph = [g for g in agent_graphs if g.id == listing.agentId][0] + graph = [g for g in agent_graphs if g.id == listing.agentGraphId][0] version = await db.storelistingversion.create( data={ "agentGraphId": graph.id, diff --git a/autogpt_platform/backend/test/util/test_type.py b/autogpt_platform/backend/backend/util/type_test.py similarity index 100% rename from autogpt_platform/backend/test/util/test_type.py rename to autogpt_platform/backend/backend/util/type_test.py diff --git a/autogpt_platform/backend/backend/util/virus_scanner.py b/autogpt_platform/backend/backend/util/virus_scanner.py new file mode 100644 index 0000000000..4494c94790 --- /dev/null +++ b/autogpt_platform/backend/backend/util/virus_scanner.py @@ -0,0 +1,209 @@ +import asyncio +import io +import logging +import time +from typing import Optional, Tuple + +import aioclamd +from pydantic import BaseModel +from pydantic_settings import BaseSettings + +from backend.util.settings import Settings + +logger = logging.getLogger(__name__) +settings = Settings() + + +class VirusScanResult(BaseModel): + is_clean: bool + scan_time_ms: int + file_size: int + threat_name: Optional[str] = None + + +class VirusScannerSettings(BaseSettings): + # Tunables for the scanner layer (NOT the ClamAV daemon). + clamav_service_host: str = "localhost" + clamav_service_port: int = 3310 + clamav_service_timeout: int = 60 + clamav_service_enabled: bool = True + # If the service is disabled, all files are considered clean. + mark_failed_scans_as_clean: bool = False + # Client-side protective limits + max_scan_size: int = 2 * 1024 * 1024 * 1024 # 2 GB guard-rail in memory + min_chunk_size: int = 128 * 1024 # 128 KB hard floor + max_retries: int = 8 # halve ≤ max_retries times + # Concurrency throttle toward the ClamAV daemon. Do *NOT* simply turn this + # up to the number of CPU cores; keep it ≤ (MaxThreads / pods) – 1. + max_concurrency: int = 5 + + +class VirusScannerService: + """Fully-async ClamAV wrapper using **aioclamd**. + + • Reuses a single `ClamdAsyncClient` connection (aioclamd keeps the socket open). + • Throttles concurrent `INSTREAM` calls with an `asyncio.Semaphore` so we don't exhaust daemon worker threads or file descriptors. + • Falls back to progressively smaller chunk sizes when the daemon rejects a stream as too large. + """ + + def __init__(self, settings: VirusScannerSettings) -> None: + self.settings = settings + self._client = aioclamd.ClamdAsyncClient( + host=settings.clamav_service_host, + port=settings.clamav_service_port, + timeout=settings.clamav_service_timeout, + ) + self._sem = asyncio.Semaphore(settings.max_concurrency) + + # ------------------------------------------------------------------ # + # Helpers + # ------------------------------------------------------------------ # + + @staticmethod + def _parse_raw(raw: Optional[dict]) -> Tuple[bool, Optional[str]]: + """ + Convert aioclamd output to (infected?, threat_name). + Returns (False, None) for clean. + """ + if not raw: + return False, None + status, threat = next(iter(raw.values())) + return status == "FOUND", threat + + async def _instream(self, chunk: bytes) -> Tuple[bool, Optional[str]]: + """Scan **one** chunk with concurrency control.""" + async with self._sem: + try: + raw = await self._client.instream(io.BytesIO(chunk)) + return self._parse_raw(raw) + except (BrokenPipeError, ConnectionResetError) as exc: + raise RuntimeError("size-limit") from exc + except Exception as exc: + if "INSTREAM size limit exceeded" in str(exc): + raise RuntimeError("size-limit") from exc + raise + + # ------------------------------------------------------------------ # + # Public API + # ------------------------------------------------------------------ # + + async def scan_file( + self, content: bytes, *, filename: str = "unknown" + ) -> VirusScanResult: + """ + Scan `content`. Returns a result object or raises on infrastructure + failure (unreachable daemon, etc.). + The algorithm always tries whole-file first. If the daemon refuses + on size grounds, it falls back to chunked parallel scanning. + """ + if not self.settings.clamav_service_enabled: + logger.warning(f"Virus scanning disabled – accepting {filename}") + return VirusScanResult( + is_clean=True, scan_time_ms=0, file_size=len(content) + ) + if len(content) > self.settings.max_scan_size: + logger.warning( + f"File {filename} ({len(content)} bytes) exceeds client max scan size ({self.settings.max_scan_size}); Stopping virus scan" + ) + return VirusScanResult( + is_clean=self.settings.mark_failed_scans_as_clean, + file_size=len(content), + scan_time_ms=0, + ) + + # Ensure daemon is reachable (small RTT check) + if not await self._client.ping(): + raise RuntimeError("ClamAV service is unreachable") + + start = time.monotonic() + chunk_size = len(content) # Start with full content length + for retry in range(self.settings.max_retries): + # For small files, don't check min_chunk_size limit + if chunk_size < self.settings.min_chunk_size and chunk_size < len(content): + break + logger.debug( + f"Scanning {filename} with chunk size: {chunk_size // 1_048_576} MB (retry {retry + 1}/{self.settings.max_retries})" + ) + try: + tasks = [ + asyncio.create_task(self._instream(content[o : o + chunk_size])) + for o in range(0, len(content), chunk_size) + ] + for coro in asyncio.as_completed(tasks): + infected, threat = await coro + if infected: + for t in tasks: + if not t.done(): + t.cancel() + return VirusScanResult( + is_clean=False, + threat_name=threat, + file_size=len(content), + scan_time_ms=int((time.monotonic() - start) * 1000), + ) + # All chunks clean + return VirusScanResult( + is_clean=True, + file_size=len(content), + scan_time_ms=int((time.monotonic() - start) * 1000), + ) + except RuntimeError as exc: + if str(exc) == "size-limit": + chunk_size //= 2 + continue + logger.error(f"Cannot scan {filename}: {exc}") + raise + # Phase 3 – give up but warn + logger.warning( + f"Unable to virus scan {filename} ({len(content)} bytes) even with minimum chunk size ({self.settings.min_chunk_size} bytes). Recommend manual review." + ) + return VirusScanResult( + is_clean=self.settings.mark_failed_scans_as_clean, + file_size=len(content), + scan_time_ms=int((time.monotonic() - start) * 1000), + ) + + +_scanner: Optional[VirusScannerService] = None + + +def get_virus_scanner() -> VirusScannerService: + global _scanner + if _scanner is None: + _settings = VirusScannerSettings( + clamav_service_host=settings.config.clamav_service_host, + clamav_service_port=settings.config.clamav_service_port, + clamav_service_enabled=settings.config.clamav_service_enabled, + max_concurrency=settings.config.clamav_max_concurrency, + mark_failed_scans_as_clean=settings.config.clamav_mark_failed_scans_as_clean, + ) + _scanner = VirusScannerService(_settings) + return _scanner + + +async def scan_content_safe(content: bytes, *, filename: str = "unknown") -> None: + """ + Helper function to scan content and raise appropriate exceptions. + + Raises: + VirusDetectedError: If virus is found + VirusScanError: If scanning fails + """ + from backend.server.v2.store.exceptions import VirusDetectedError, VirusScanError + + try: + result = await get_virus_scanner().scan_file(content, filename=filename) + if not result.is_clean: + threat_name = result.threat_name or "Unknown threat" + logger.warning(f"Virus detected in file {filename}: {threat_name}") + raise VirusDetectedError( + threat_name, f"File rejected due to virus detection: {threat_name}" + ) + + logger.info(f"File {filename} passed virus scan in {result.scan_time_ms}ms") + + except VirusDetectedError: + raise + except Exception as e: + logger.error(f"Virus scanning failed for {filename}: {str(e)}") + raise VirusScanError(f"Virus scanning failed: {str(e)}") from e diff --git a/autogpt_platform/backend/backend/util/virus_scanner_test.py b/autogpt_platform/backend/backend/util/virus_scanner_test.py new file mode 100644 index 0000000000..81b5ad3342 --- /dev/null +++ b/autogpt_platform/backend/backend/util/virus_scanner_test.py @@ -0,0 +1,253 @@ +import asyncio +from unittest.mock import AsyncMock, Mock, patch + +import pytest + +from backend.server.v2.store.exceptions import VirusDetectedError, VirusScanError +from backend.util.virus_scanner import ( + VirusScannerService, + VirusScannerSettings, + VirusScanResult, + get_virus_scanner, + scan_content_safe, +) + + +class TestVirusScannerService: + @pytest.fixture + def scanner_settings(self): + return VirusScannerSettings( + clamav_service_host="localhost", + clamav_service_port=3310, + clamav_service_enabled=True, + max_scan_size=10 * 1024 * 1024, # 10MB for testing + mark_failed_scans_as_clean=False, # For testing, failed scans should be clean + ) + + @pytest.fixture + def scanner(self, scanner_settings): + return VirusScannerService(scanner_settings) + + @pytest.fixture + def disabled_scanner(self): + settings = VirusScannerSettings(clamav_service_enabled=False) + return VirusScannerService(settings) + + def test_scanner_initialization(self, scanner_settings): + scanner = VirusScannerService(scanner_settings) + assert scanner.settings.clamav_service_host == "localhost" + assert scanner.settings.clamav_service_port == 3310 + assert scanner.settings.clamav_service_enabled is True + + @pytest.mark.asyncio + async def test_scan_disabled_returns_clean(self, disabled_scanner): + content = b"test file content" + result = await disabled_scanner.scan_file(content, filename="test.txt") + + assert result.is_clean is True + assert result.threat_name is None + assert result.file_size == len(content) + assert result.scan_time_ms == 0 + + @pytest.mark.asyncio + async def test_scan_file_too_large(self, scanner): + # Create content larger than max_scan_size + large_content = b"x" * (scanner.settings.max_scan_size + 1) + + # Large files behavior depends on mark_failed_scans_as_clean setting + result = await scanner.scan_file(large_content, filename="large_file.txt") + assert result.is_clean == scanner.settings.mark_failed_scans_as_clean + assert result.file_size == len(large_content) + assert result.scan_time_ms == 0 + + @pytest.mark.asyncio + async def test_scan_file_too_large_both_configurations(self): + """Test large file handling with both mark_failed_scans_as_clean configurations""" + large_content = b"x" * (10 * 1024 * 1024 + 1) # Larger than 10MB + + # Test with mark_failed_scans_as_clean=True + settings_clean = VirusScannerSettings( + max_scan_size=10 * 1024 * 1024, mark_failed_scans_as_clean=True + ) + scanner_clean = VirusScannerService(settings_clean) + result_clean = await scanner_clean.scan_file( + large_content, filename="large_file.txt" + ) + assert result_clean.is_clean is True + + # Test with mark_failed_scans_as_clean=False + settings_dirty = VirusScannerSettings( + max_scan_size=10 * 1024 * 1024, mark_failed_scans_as_clean=False + ) + scanner_dirty = VirusScannerService(settings_dirty) + result_dirty = await scanner_dirty.scan_file( + large_content, filename="large_file.txt" + ) + assert result_dirty.is_clean is False + + # Note: ping method was removed from current implementation + + @pytest.mark.asyncio + async def test_scan_clean_file(self, scanner): + async def mock_instream(_): + await asyncio.sleep(0.001) # Small delay to ensure timing > 0 + return None # No virus detected + + mock_client = Mock() + mock_client.ping = AsyncMock(return_value=True) + mock_client.instream = AsyncMock(side_effect=mock_instream) + + # Replace the client instance that was created in the constructor + scanner._client = mock_client + + content = b"clean file content" + result = await scanner.scan_file(content, filename="clean.txt") + + assert result.is_clean is True + assert result.threat_name is None + assert result.file_size == len(content) + assert result.scan_time_ms > 0 + + @pytest.mark.asyncio + async def test_scan_infected_file(self, scanner): + async def mock_instream(_): + await asyncio.sleep(0.001) # Small delay to ensure timing > 0 + return {"stream": ("FOUND", "Win.Test.EICAR_HDB-1")} + + mock_client = Mock() + mock_client.ping = AsyncMock(return_value=True) + mock_client.instream = AsyncMock(side_effect=mock_instream) + + # Replace the client instance that was created in the constructor + scanner._client = mock_client + + content = b"infected file content" + result = await scanner.scan_file(content, filename="infected.txt") + + assert result.is_clean is False + assert result.threat_name == "Win.Test.EICAR_HDB-1" + assert result.file_size == len(content) + assert result.scan_time_ms > 0 + + @pytest.mark.asyncio + async def test_scan_clamav_unavailable_fail_safe(self, scanner): + mock_client = Mock() + mock_client.ping = AsyncMock(return_value=False) + + # Replace the client instance that was created in the constructor + scanner._client = mock_client + + content = b"test content" + + with pytest.raises(RuntimeError, match="ClamAV service is unreachable"): + await scanner.scan_file(content, filename="test.txt") + + @pytest.mark.asyncio + async def test_scan_error_fail_safe(self, scanner): + mock_client = Mock() + mock_client.ping = AsyncMock(return_value=True) + mock_client.instream = AsyncMock(side_effect=Exception("Scanning error")) + + # Replace the client instance that was created in the constructor + scanner._client = mock_client + + content = b"test content" + + with pytest.raises(Exception, match="Scanning error"): + await scanner.scan_file(content, filename="test.txt") + + # Note: scan_file_method and scan_upload_file tests removed as these APIs don't exist in current implementation + + def test_get_virus_scanner_singleton(self): + scanner1 = get_virus_scanner() + scanner2 = get_virus_scanner() + + # Should return the same instance + assert scanner1 is scanner2 + + # Note: client_reuse test removed as _get_client method doesn't exist in current implementation + + def test_scan_result_model(self): + # Test VirusScanResult model + result = VirusScanResult( + is_clean=False, threat_name="Test.Virus", scan_time_ms=150, file_size=1024 + ) + + assert result.is_clean is False + assert result.threat_name == "Test.Virus" + assert result.scan_time_ms == 150 + assert result.file_size == 1024 + + @pytest.mark.asyncio + async def test_concurrent_scans(self, scanner): + async def mock_instream(_): + await asyncio.sleep(0.001) # Small delay to ensure timing > 0 + return None + + mock_client = Mock() + mock_client.ping = AsyncMock(return_value=True) + mock_client.instream = AsyncMock(side_effect=mock_instream) + + # Replace the client instance that was created in the constructor + scanner._client = mock_client + + content1 = b"file1 content" + content2 = b"file2 content" + + # Run concurrent scans + results = await asyncio.gather( + scanner.scan_file(content1, filename="file1.txt"), + scanner.scan_file(content2, filename="file2.txt"), + ) + + assert len(results) == 2 + assert all(result.is_clean for result in results) + assert results[0].file_size == len(content1) + assert results[1].file_size == len(content2) + assert all(result.scan_time_ms > 0 for result in results) + + +class TestHelperFunctions: + """Test the helper functions scan_content_safe""" + + @pytest.mark.asyncio + async def test_scan_content_safe_clean(self): + """Test scan_content_safe with clean content""" + with patch("backend.util.virus_scanner.get_virus_scanner") as mock_get_scanner: + mock_scanner = Mock() + mock_scanner.scan_file = AsyncMock() + mock_scanner.scan_file.return_value = Mock( + is_clean=True, threat_name=None, scan_time_ms=50, file_size=100 + ) + mock_get_scanner.return_value = mock_scanner + + # Should not raise any exception + await scan_content_safe(b"clean content", filename="test.txt") + + @pytest.mark.asyncio + async def test_scan_content_safe_infected(self): + """Test scan_content_safe with infected content""" + with patch("backend.util.virus_scanner.get_virus_scanner") as mock_get_scanner: + mock_scanner = Mock() + mock_scanner.scan_file = AsyncMock() + mock_scanner.scan_file.return_value = Mock( + is_clean=False, threat_name="Test.Virus", scan_time_ms=50, file_size=100 + ) + mock_get_scanner.return_value = mock_scanner + + with pytest.raises(VirusDetectedError) as exc_info: + await scan_content_safe(b"infected content", filename="virus.txt") + + assert exc_info.value.threat_name == "Test.Virus" + + @pytest.mark.asyncio + async def test_scan_content_safe_scan_error(self): + """Test scan_content_safe when scanning fails""" + with patch("backend.util.virus_scanner.get_virus_scanner") as mock_get_scanner: + mock_scanner = Mock() + mock_scanner.scan_file = AsyncMock() + mock_scanner.scan_file.side_effect = Exception("Scan failed") + mock_get_scanner.return_value = mock_scanner + + with pytest.raises(VirusScanError, match="Virus scanning failed"): + await scan_content_safe(b"test content", filename="test.txt") diff --git a/autogpt_platform/backend/migrations/20250620140815_add_preset_webhook_relation/migration.sql b/autogpt_platform/backend/migrations/20250620140815_add_preset_webhook_relation/migration.sql new file mode 100644 index 0000000000..6720d796a9 --- /dev/null +++ b/autogpt_platform/backend/migrations/20250620140815_add_preset_webhook_relation/migration.sql @@ -0,0 +1,5 @@ +-- Add webhookId column +ALTER TABLE "AgentPreset" ADD COLUMN "webhookId" TEXT; + +-- Add AgentPreset<->IntegrationWebhook relation +ALTER TABLE "AgentPreset" ADD CONSTRAINT "AgentPreset_webhookId_fkey" FOREIGN KEY ("webhookId") REFERENCES "IntegrationWebhook"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/autogpt_platform/backend/poetry.lock b/autogpt_platform/backend/poetry.lock index 4ea22878d4..b3312f7c39 100644 --- a/autogpt_platform/backend/poetry.lock +++ b/autogpt_platform/backend/poetry.lock @@ -17,6 +17,18 @@ aiormq = ">=6.8,<6.9" exceptiongroup = ">=1,<2" yarl = "*" +[[package]] +name = "aioclamd" +version = "1.0.0" +description = "Asynchronous client for virus scanning with ClamAV" +optional = false +python-versions = ">=3.7,<4.0" +groups = ["main"] +files = [ + {file = "aioclamd-1.0.0-py3-none-any.whl", hash = "sha256:4727da3953a4b38be4c2de1acb6b3bb3c94c1c171dcac780b80234ee6253f3d9"}, + {file = "aioclamd-1.0.0.tar.gz", hash = "sha256:7b14e94e3a2285cc89e2f4d434e2a01f322d3cb95476ce2dda015a7980876047"}, +] + [[package]] name = "aiodns" version = "3.4.0" @@ -5006,6 +5018,27 @@ statsig = ["statsig (>=0.55.3)"] tornado = ["tornado (>=6)"] unleash = ["UnleashClient (>=6.0.1)"] +[[package]] +name = "setuptools" +version = "80.9.0" +description = "Easily download, build, install, upgrade, and uninstall Python packages" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "setuptools-80.9.0-py3-none-any.whl", hash = "sha256:062d34222ad13e0cc312a4c02d73f059e86a4acbfbdea8f8f76b28c99f306922"}, + {file = "setuptools-80.9.0.tar.gz", hash = "sha256:f36b47402ecde768dbfafc46e8e4207b4360c654f1f3bb84475f0a28628fb19c"}, +] + +[package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1) ; sys_platform != \"cygwin\"", "ruff (>=0.8.0) ; sys_platform != \"cygwin\""] +core = ["importlib_metadata (>=6) ; python_version < \"3.10\"", "jaraco.functools (>=4)", "jaraco.text (>=3.7)", "more_itertools", "more_itertools (>=8.8)", "packaging (>=24.2)", "platformdirs (>=4.2.2)", "tomli (>=2.0.1) ; python_version < \"3.11\"", "wheel (>=0.43.0)"] +cover = ["pytest-cov"] +doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "pyproject-hooks (!=1.1)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-inline-tabs", "sphinx-lint", "sphinx-notfound-page (>=1,<2)", "sphinx-reredirects", "sphinxcontrib-towncrier", "towncrier (<24.7)"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["build[virtualenv] (>=1.0.3)", "filelock (>=3.4.0)", "ini2toml[lite] (>=0.14)", "jaraco.develop (>=7.21) ; python_version >= \"3.9\" and sys_platform != \"cygwin\"", "jaraco.envs (>=2.2)", "jaraco.path (>=3.7.2)", "jaraco.test (>=5.5)", "packaging (>=24.2)", "pip (>=19.1)", "pyproject-hooks (!=1.1)", "pytest (>=6,!=8.1.*)", "pytest-home (>=0.5)", "pytest-perf ; sys_platform != \"cygwin\"", "pytest-subprocess", "pytest-timeout", "pytest-xdist (>=3)", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel (>=0.44.0)"] +type = ["importlib_metadata (>=7.0.2) ; python_version < \"3.10\"", "jaraco.develop (>=7.21) ; sys_platform != \"cygwin\"", "mypy (==1.14.*)", "pytest-mypy"] + [[package]] name = "sgmllib3k" version = "1.0.0" @@ -6369,4 +6402,4 @@ cffi = ["cffi (>=1.11)"] [metadata] lock-version = "2.1" python-versions = ">=3.10,<3.13" -content-hash = "6c93e51cf22c06548aa6d0e23ca8ceb4450f5e02d4142715e941aabc1a2cbd6a" +content-hash = "b5c1201f27ee8d05d5d8c89702123df4293f124301d1aef7451591a351872260" diff --git a/autogpt_platform/backend/pyproject.toml b/autogpt_platform/backend/pyproject.toml index 26c05668c0..30429152cb 100644 --- a/autogpt_platform/backend/pyproject.toml +++ b/autogpt_platform/backend/pyproject.toml @@ -68,6 +68,9 @@ zerobouncesdk = "^1.1.1" # NOTE: please insert new dependencies in their alphabetical location pytest-snapshot = "^0.9.0" aiofiles = "^24.1.0" +tiktoken = "^0.9.0" +aioclamd = "^1.0.0" +setuptools = "^80.9.0" [tool.poetry.group.dev.dependencies] aiohappyeyeballs = "^2.6.1" @@ -112,6 +115,11 @@ ignore_patterns = [] [tool.pytest.ini_options] asyncio_mode = "auto" +asyncio_default_fixture_loop_scope = "session" +filterwarnings = [ + "ignore:'audioop' is deprecated:DeprecationWarning:discord.player", + "ignore:invalid escape sequence:DeprecationWarning:tweepy.api", +] [tool.ruff] target-version = "py310" diff --git a/autogpt_platform/backend/schema.prisma b/autogpt_platform/backend/schema.prisma index 58e724db9c..0282b51f98 100644 --- a/autogpt_platform/backend/schema.prisma +++ b/autogpt_platform/backend/schema.prisma @@ -169,6 +169,10 @@ model AgentPreset { InputPresets AgentNodeExecutionInputOutput[] @relation("AgentPresetsInputData") Executions AgentGraphExecution[] + // For webhook-triggered agents: reference to the webhook that triggers the agent + webhookId String? + Webhook IntegrationWebhook? @relation(fields: [webhookId], references: [id]) + isDeleted Boolean @default(false) @@index([userId]) @@ -428,7 +432,8 @@ model IntegrationWebhook { providerWebhookId String // Webhook ID assigned by the provider - AgentNodes AgentNode[] + AgentNodes AgentNode[] + AgentPresets AgentPreset[] @@index([userId]) } diff --git a/autogpt_platform/backend/snapshots/analytics_log_metric_success_improved b/autogpt_platform/backend/snapshots/analytics_log_metric_success_improved new file mode 100644 index 0000000000..a08f406588 --- /dev/null +++ b/autogpt_platform/backend/snapshots/analytics_log_metric_success_improved @@ -0,0 +1,3 @@ +{ + "metric_id": "metric-123-uuid" +} \ No newline at end of file diff --git a/autogpt_platform/backend/snapshots/analytics_metric_float_precision b/autogpt_platform/backend/snapshots/analytics_metric_float_precision new file mode 100644 index 0000000000..8d3319fb8c --- /dev/null +++ b/autogpt_platform/backend/snapshots/analytics_metric_float_precision @@ -0,0 +1,4 @@ +{ + "metric_id": "metric-float_precision-uuid", + "test_case": "float_precision" +} \ No newline at end of file diff --git a/autogpt_platform/backend/snapshots/analytics_metric_integer_value b/autogpt_platform/backend/snapshots/analytics_metric_integer_value new file mode 100644 index 0000000000..113a602abf --- /dev/null +++ b/autogpt_platform/backend/snapshots/analytics_metric_integer_value @@ -0,0 +1,4 @@ +{ + "metric_id": "metric-integer_value-uuid", + "test_case": "integer_value" +} \ No newline at end of file diff --git a/autogpt_platform/backend/snapshots/analytics_metric_large_number b/autogpt_platform/backend/snapshots/analytics_metric_large_number new file mode 100644 index 0000000000..8ad9f60a66 --- /dev/null +++ b/autogpt_platform/backend/snapshots/analytics_metric_large_number @@ -0,0 +1,4 @@ +{ + "metric_id": "metric-large_number-uuid", + "test_case": "large_number" +} \ No newline at end of file diff --git a/autogpt_platform/backend/snapshots/analytics_metric_negative_value b/autogpt_platform/backend/snapshots/analytics_metric_negative_value new file mode 100644 index 0000000000..427967ee04 --- /dev/null +++ b/autogpt_platform/backend/snapshots/analytics_metric_negative_value @@ -0,0 +1,4 @@ +{ + "metric_id": "metric-negative_value-uuid", + "test_case": "negative_value" +} \ No newline at end of file diff --git a/autogpt_platform/backend/snapshots/analytics_metric_tiny_number b/autogpt_platform/backend/snapshots/analytics_metric_tiny_number new file mode 100644 index 0000000000..c7543df027 --- /dev/null +++ b/autogpt_platform/backend/snapshots/analytics_metric_tiny_number @@ -0,0 +1,4 @@ +{ + "metric_id": "metric-tiny_number-uuid", + "test_case": "tiny_number" +} \ No newline at end of file diff --git a/autogpt_platform/backend/snapshots/analytics_metric_zero_value b/autogpt_platform/backend/snapshots/analytics_metric_zero_value new file mode 100644 index 0000000000..482cbf0f6c --- /dev/null +++ b/autogpt_platform/backend/snapshots/analytics_metric_zero_value @@ -0,0 +1,4 @@ +{ + "metric_id": "metric-zero_value-uuid", + "test_case": "zero_value" +} \ No newline at end of file diff --git a/autogpt_platform/backend/snapshots/lib_agts_search b/autogpt_platform/backend/snapshots/lib_agts_search index d5698fb54c..e0f168ddd7 100644 --- a/autogpt_platform/backend/snapshots/lib_agts_search +++ b/autogpt_platform/backend/snapshots/lib_agts_search @@ -15,6 +15,12 @@ "type": "object", "properties": {} }, + "credentials_input_schema": { + "type": "object", + "properties": {} + }, + "has_external_trigger": false, + "trigger_setup_info": null, "new_output": false, "can_access_graph": true, "is_latest_version": true @@ -34,6 +40,12 @@ "type": "object", "properties": {} }, + "credentials_input_schema": { + "type": "object", + "properties": {} + }, + "has_external_trigger": false, + "trigger_setup_info": null, "new_output": false, "can_access_graph": false, "is_latest_version": true diff --git a/autogpt_platform/backend/test/__init__.py b/autogpt_platform/backend/test/__init__.py deleted file mode 100644 index d10438719d..0000000000 --- a/autogpt_platform/backend/test/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -import os - -os.environ["ENABLE_AUTH"] = "false" diff --git a/autogpt_platform/backend/test/util/test_decorator.py b/autogpt_platform/backend/test/util/test_decorator.py deleted file mode 100644 index de38b747d8..0000000000 --- a/autogpt_platform/backend/test/util/test_decorator.py +++ /dev/null @@ -1,26 +0,0 @@ -import time - -from backend.util.decorator import error_logged, time_measured - - -@time_measured -def example_function(a: int, b: int, c: int) -> int: - time.sleep(0.5) - return a + b + c - - -@error_logged -def example_function_with_error(a: int, b: int, c: int) -> int: - raise ValueError("This is a test error") - - -def test_timer_decorator(): - info, res = example_function(1, 2, 3) - assert info.cpu_time >= 0 - assert info.wall_time >= 0.4 - assert res == 6 - - -def test_error_decorator(): - res = example_function_with_error(1, 2, 3) - assert res is None diff --git a/autogpt_platform/docker-compose.platform.yml b/autogpt_platform/docker-compose.platform.yml index cc5e8c3d6a..ca9a483b40 100644 --- a/autogpt_platform/docker-compose.platform.yml +++ b/autogpt_platform/docker-compose.platform.yml @@ -93,7 +93,7 @@ services: - SCHEDULER_HOST=scheduler_server - EXECUTIONMANAGER_HOST=executor - NOTIFICATIONMANAGER_HOST=rest_server - - FRONTEND_BASE_URL=http://localhost:3000 + - NEXT_PUBLIC_FRONTEND_BASE_URL=http://localhost:3000 - BACKEND_CORS_ALLOW_ORIGINS=["http://localhost:3000"] - ENCRYPTION_KEY=dvziYgz0KSK8FENhju0ZYi8-fRTfAdlz6YLhdB_jhNw= # DO NOT USE IN PRODUCTION!! - UNSUBSCRIBE_SECRET_KEY=HlP8ivStJjmbf6NKi78m_3FnOogut0t5ckzjsIqeaio= # DO NOT USE IN PRODUCTION!! diff --git a/autogpt_platform/docker-compose.yml b/autogpt_platform/docker-compose.yml index 1fcce8e24c..07810424ed 100644 --- a/autogpt_platform/docker-compose.yml +++ b/autogpt_platform/docker-compose.yml @@ -6,6 +6,7 @@ networks: volumes: supabase-config: + clamav-data: x-agpt-services: &agpt-services @@ -63,6 +64,26 @@ services: file: ./docker-compose.platform.yml service: scheduler_server + clamav: + <<: *agpt-services + image: clamav/clamav-debian:latest + ports: + - "3310:3310" + volumes: + - clamav-data:/var/lib/clamav + environment: + - CLAMAV_NO_FRESHCLAMD=false + - CLAMD_CONF_StreamMaxLength=50M + - CLAMD_CONF_MaxFileSize=100M + - CLAMD_CONF_MaxScanSize=100M + - CLAMD_CONF_MaxThreads=12 + - CLAMD_CONF_ReadTimeout=300 + healthcheck: + test: ["CMD-SHELL", "clamdscan --version || exit 1"] + interval: 30s + timeout: 10s + retries: 3 + # frontend: # <<: *agpt-services # extends: @@ -162,3 +183,4 @@ services: - vector - redis - rabbitmq + - clamav diff --git a/autogpt_platform/frontend/.env.example b/autogpt_platform/frontend/.env.example index 0660979013..287efe5f22 100644 --- a/autogpt_platform/frontend/.env.example +++ b/autogpt_platform/frontend/.env.example @@ -1,4 +1,4 @@ -FRONTEND_BASE_URL=http://localhost:3000 +NEXT_PUBLIC_FRONTEND_BASE_URL=http://localhost:3000 NEXT_PUBLIC_AUTH_CALLBACK_URL=http://localhost:8006/auth/callback NEXT_PUBLIC_AGPT_SERVER_URL=http://localhost:8006/api @@ -23,7 +23,7 @@ NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyAgCiAgICAic ## OAuth Callback URL ## This should be {domain}/auth/callback ## Only used if you're using Supabase and OAuth -AUTH_CALLBACK_URL="${FRONTEND_BASE_URL}/auth/callback" +AUTH_CALLBACK_URL="${NEXT_PUBLIC_FRONTEND_BASE_URL}/auth/callback" GA_MEASUREMENT_ID=G-FH2XK2W4GN # When running locally, set NEXT_PUBLIC_BEHAVE_AS=CLOUD to use the a locally hosted marketplace (as is typical in development, and the cloud deployment), otherwise set it to LOCAL to have the marketplace open in a new tab diff --git a/autogpt_platform/frontend/.storybook/main.ts b/autogpt_platform/frontend/.storybook/main.ts index 41553c196d..abc97a1516 100644 --- a/autogpt_platform/frontend/.storybook/main.ts +++ b/autogpt_platform/frontend/.storybook/main.ts @@ -5,6 +5,7 @@ const config: StorybookConfig = { "../src/components/overview.stories.@(js|jsx|mjs|ts|tsx)", "../src/components/tokens/**/*.stories.@(js|jsx|mjs|ts|tsx)", "../src/components/atoms/**/*.stories.@(js|jsx|mjs|ts|tsx)", + "../src/components/molecules/**/*.stories.@(js|jsx|mjs|ts|tsx)", "../src/components/agptui/**/*.stories.@(js|jsx|mjs|ts|tsx)", ], addons: [ diff --git a/autogpt_platform/frontend/README.md b/autogpt_platform/frontend/README.md index 18fda747d4..94c65a6e3b 100644 --- a/autogpt_platform/frontend/README.md +++ b/autogpt_platform/frontend/README.md @@ -63,9 +63,150 @@ Every time a new Front-end dependency is added by you or others, you will need t - `pnpm type-check` - Run TypeScript type checking - `pnpm test` - Run Playwright tests - `pnpm test-ui` - Run Playwright tests with UI +- `pnpm fetch:openapi` - Fetch OpenAPI spec from backend +- `pnpm generate:api-client` - Generate API client from OpenAPI spec +- `pnpm generate:api-all` - Fetch OpenAPI spec and generate API client This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. +## 🔄 Data Fetching Strategy + +> [!NOTE] +> You don't need to run the OpenAPI commands below to run the Front-end. You will only need to run them when adding or modifying endpoints on the Backend API and wanting to use those on the Frontend. + +This project uses an auto-generated API client powered by [**Orval**](https://orval.dev/), which creates type-safe API clients from OpenAPI specifications. + +### How It Works + +1. **Backend Requirements**: Each API endpoint needs a summary and tag in the OpenAPI spec +2. **Operation ID Generation**: FastAPI generates operation IDs using the pattern `{method}{tag}{summary}` +3. **Spec Fetching**: The OpenAPI spec is fetched from `http://localhost:8006/openapi.json` and saved to the frontend +4. **Spec Transformation**: The OpenAPI spec is cleaned up using a custom transformer (see `autogpt_platform/frontend/src/app/api/transformers`) +5. **Client Generation**: Auto-generated client includes TypeScript types, API endpoints, and Zod schemas, organized by tags + +### API Client Commands + +```bash +# Fetch OpenAPI spec from backend and generate client +pnpm generate:api-all + +# Only fetch the OpenAPI spec +pnpm fetch:openapi + +# Only generate the client (after spec is fetched) +pnpm generate:api-client +``` + +### Using the Generated Client + +The generated client provides React Query hooks for both queries and mutations: + +#### Queries (GET requests) + +```typescript +import { useGetV1GetNotificationPreferences } from "@/app/api/__generated__/endpoints/auth/auth"; + +const { data, isLoading, isError } = useGetV1GetNotificationPreferences({ + query: { + select: (res) => res.data, + // Other React Query options + }, +}); +``` + +#### Mutations (POST, PUT, DELETE requests) + +```typescript +import { useDeleteV2DeleteStoreSubmission } from "@/app/api/__generated__/endpoints/store/store"; +import { getGetV2ListMySubmissionsQueryKey } from "@/app/api/__generated__/endpoints/store/store"; +import { useQueryClient } from "@tanstack/react-query"; + +const queryClient = useQueryClient(); + +const { mutateAsync: deleteSubmission } = useDeleteV2DeleteStoreSubmission({ + mutation: { + onSuccess: () => { + // Invalidate related queries to refresh data + queryClient.invalidateQueries({ + queryKey: getGetV2ListMySubmissionsQueryKey(), + }); + }, + }, +}); + +// Usage +await deleteSubmission({ + submissionId: submission_id, +}); +``` + +#### Server Actions + +For server-side operations, you can also use the generated client functions directly: + +```typescript +import { postV1UpdateNotificationPreferences } from "@/app/api/__generated__/endpoints/auth/auth"; + +// In a server action +const preferences = { + email: "user@example.com", + preferences: { + AGENT_RUN: true, + ZERO_BALANCE: false, + // ... other preferences + }, + daily_limit: 0, +}; + +await postV1UpdateNotificationPreferences(preferences); +``` + +#### Server-Side Prefetching + +For server-side components, you can prefetch data on the server and hydrate it in the client cache. This allows immediate access to cached data when queries are called: + +```typescript +import { getQueryClient } from "@/lib/tanstack-query/getQueryClient"; +import { + prefetchGetV2ListStoreAgentsQuery, + prefetchGetV2ListStoreCreatorsQuery +} from "@/app/api/__generated__/endpoints/store/store"; +import { HydrationBoundary, dehydrate } from "@tanstack/react-query"; + +// In your server component +const queryClient = getQueryClient(); + +await Promise.all([ + prefetchGetV2ListStoreAgentsQuery(queryClient, { + featured: true, + }), + prefetchGetV2ListStoreAgentsQuery(queryClient, { + sorted_by: "runs", + }), + prefetchGetV2ListStoreCreatorsQuery(queryClient, { + featured: true, + sorted_by: "num_agents", + }), +]); + +return ( + + + +); +``` + +This pattern improves performance by serving pre-fetched data from the server while maintaining the benefits of client-side React Query features. + +### Configuration + +The Orval configuration is located in `autogpt_platform/frontend/orval.config.ts`. It generates two separate clients: + +1. **autogpt_api_client**: React Query hooks for client-side data fetching +2. **autogpt_zod_schema**: Zod schemas for validation + +For more details, see the [Orval documentation](https://orval.dev/) or check the configuration file. + ## 🚚 Deploy TODO @@ -106,12 +247,6 @@ Storybook is a powerful development environment for UI components. It allows you pnpm test-storybook ``` - For CI environments, use: - - ```bash - pnpm test-storybook:ci - ``` - 4. **Writing Stories**: Create `.stories.tsx` files alongside your components to define different states and variations of your components. diff --git a/autogpt_platform/frontend/orval.config.ts b/autogpt_platform/frontend/orval.config.ts index afce23a441..0047ecb720 100644 --- a/autogpt_platform/frontend/orval.config.ts +++ b/autogpt_platform/frontend/orval.config.ts @@ -3,24 +3,19 @@ import { defineConfig } from "orval"; export default defineConfig({ autogpt_api_client: { input: { - target: `./src/api/openapi.json`, + target: `./src/app/api/openapi.json`, override: { - transformer: "./src/api/transformers/fix-tags.mjs", + transformer: "./src/app/api/transformers/fix-tags.mjs", }, }, output: { - workspace: "./src/api", + workspace: "./src/app/api", target: `./__generated__/endpoints`, schemas: "./__generated__/models", mode: "tags-split", client: "react-query", httpClient: "fetch", indexFiles: false, - mock: { - type: "msw", - delay: 1000, // artifical latency - generateEachHttpStatus: true, // helps us test error-handling scenarios and generate mocks for all HTTP statuses - }, override: { mutator: { path: "./mutators/custom-mutator.ts", @@ -31,29 +26,37 @@ export default defineConfig({ useMutation: true, // Will add more as their use cases arise }, + operations: { + "getV2List library agents": { + query: { + useInfinite: true, + useInfiniteQueryParam: "page", + }, + }, + }, }, }, hooks: { afterAllFilesWrite: "prettier --write", }, }, - autogpt_zod_schema: { - input: { - target: `./src/api/openapi.json`, - override: { - transformer: "./src/api/transformers/fix-tags.mjs", - }, - }, - output: { - workspace: "./src/api", - target: `./__generated__/zod-schema`, - schemas: "./__generated__/models", - mode: "tags-split", - client: "zod", - indexFiles: false, - }, - hooks: { - afterAllFilesWrite: "prettier --write", - }, - }, + // autogpt_zod_schema: { + // input: { + // target: `./src/app/api/openapi.json`, + // override: { + // transformer: "./src/app/api/transformers/fix-tags.mjs", + // }, + // }, + // output: { + // workspace: "./src/app/api", + // target: `./__generated__/zod-schema`, + // schemas: "./__generated__/models", + // mode: "tags-split", + // client: "zod", + // indexFiles: false, + // }, + // hooks: { + // afterAllFilesWrite: "prettier --write", + // }, + // }, }); diff --git a/autogpt_platform/frontend/package.json b/autogpt_platform/frontend/package.json index 39edca7d05..81ddbb969e 100644 --- a/autogpt_platform/frontend/package.json +++ b/autogpt_platform/frontend/package.json @@ -4,7 +4,6 @@ "private": true, "scripts": { "dev": "next dev --turbo", - "dev:test": "NODE_ENV=test && next dev --turbo", "build": "pnpm run generate:api-client && SKIP_STORYBOOK_TESTS=true next build", "start": "next start", "start:standalone": "cd .next/standalone && node server.js", @@ -19,7 +18,7 @@ "build-storybook": "storybook build", "test-storybook": "test-storybook", "test-storybook:ci": "concurrently -k -s first -n \"SB,TEST\" -c \"magenta,blue\" \"pnpm run build-storybook -- --quiet && npx http-server storybook-static --port 6006 --silent\" \"wait-on tcp:6006 && pnpm run test-storybook\"", - "fetch:openapi": "curl http://localhost:8006/openapi.json > ./src/api/openapi.json && prettier --write ./src/api/openapi.json", + "fetch:openapi": "curl http://localhost:8006/openapi.json > ./src/app/api/openapi.json && prettier --write ./src/app/api/openapi.json", "generate:api-client": "orval --config ./orval.config.ts", "generate:api-all": "pnpm run fetch:openapi && pnpm run generate:api-client" }, @@ -91,20 +90,21 @@ "tailwind-merge": "2.6.0", "tailwindcss-animate": "1.0.7", "uuid": "11.1.0", + "vaul": "1.1.2", "zod": "3.25.56" }, "devDependencies": { - "@chromatic-com/storybook": "4.0.0", + "@chromatic-com/storybook": "4.0.1", "@playwright/test": "1.53.1", - "@storybook/addon-a11y": "9.0.12", - "@storybook/addon-docs": "9.0.12", - "@storybook/addon-links": "9.0.12", - "@storybook/addon-onboarding": "9.0.12", - "@storybook/nextjs": "9.0.12", - "@tanstack/eslint-plugin-query": "5.78.0", - "@tanstack/react-query-devtools": "5.80.10", + "@storybook/addon-a11y": "9.0.14", + "@storybook/addon-docs": "9.0.14", + "@storybook/addon-links": "9.0.14", + "@storybook/addon-onboarding": "9.0.14", + "@storybook/nextjs": "9.0.14", + "@tanstack/eslint-plugin-query": "5.81.2", + "@tanstack/react-query-devtools": "5.81.5", "@types/canvas-confetti": "1.9.0", - "@types/lodash": "4.17.18", + "@types/lodash": "4.17.19", "@types/negotiator": "0.6.4", "@types/node": "22.15.30", "@types/react": "18.3.17", @@ -112,19 +112,19 @@ "@types/react-modal": "3.16.3", "axe-playwright": "2.1.0", "chromatic": "11.25.2", - "concurrently": "9.1.2", + "concurrently": "9.2.0", "eslint": "8.57.1", "eslint-config-next": "15.3.4", - "eslint-plugin-storybook": "9.0.12", + "eslint-plugin-storybook": "9.0.14", "import-in-the-middle": "1.14.2", "msw": "2.10.2", "msw-storybook-addon": "2.0.5", "orval": "7.10.0", "postcss": "8.5.6", - "prettier": "3.5.3", - "prettier-plugin-tailwindcss": "0.6.12", + "prettier": "3.6.2", + "prettier-plugin-tailwindcss": "0.6.13", "require-in-the-middle": "7.5.2", - "storybook": "9.0.12", + "storybook": "9.0.14", "tailwindcss": "3.4.17", "typescript": "5.8.3" }, diff --git a/autogpt_platform/frontend/playwright.config.ts b/autogpt_platform/frontend/playwright.config.ts index fddf03eeb9..607f3e15f4 100644 --- a/autogpt_platform/frontend/playwright.config.ts +++ b/autogpt_platform/frontend/playwright.config.ts @@ -34,7 +34,15 @@ export default defineConfig({ bypassCSP: true, }, /* Maximum time one test can run for */ - timeout: 60000, + timeout: 30000, + + /* Configure web server to start automatically */ + webServer: { + command: "NEXT_PUBLIC_PW_TEST=true pnpm start", + url: "http://localhost:3000", + reuseExistingServer: !process.env.CI, + timeout: 120 * 1000, + }, /* Configure projects for major browsers */ projects: [ @@ -73,15 +81,4 @@ export default defineConfig({ // use: { ...devices['Desktop Chrome'], channel: 'chrome' }, // }, ], - - /* Run your local server before starting the tests */ - webServer: { - command: "pnpm start", - url: "http://localhost:3000/", - reuseExistingServer: !process.env.CI, - timeout: 10 * 1000, - env: { - NODE_ENV: "test", - }, - }, }); diff --git a/autogpt_platform/frontend/pnpm-lock.yaml b/autogpt_platform/frontend/pnpm-lock.yaml index b3d1e7975d..0b8867ef7f 100644 --- a/autogpt_platform/frontend/pnpm-lock.yaml +++ b/autogpt_platform/frontend/pnpm-lock.yaml @@ -16,7 +16,7 @@ importers: version: 5.1.1(react-hook-form@7.57.0(react@18.3.1)) '@next/third-parties': specifier: 15.3.3 - version: 15.3.3(next@15.3.3(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) + version: 15.3.3(next@15.3.3(@babel/core@7.27.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1) '@phosphor-icons/react': specifier: 2.1.10 version: 2.1.10(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -79,7 +79,7 @@ importers: version: 1.2.7(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@sentry/nextjs': specifier: 9.27.0 - version: 9.27.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(next@15.3.3(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(webpack@5.99.9(esbuild@0.25.5)) + version: 9.27.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(next@15.3.3(@babel/core@7.27.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(webpack@5.99.9(esbuild@0.25.5)) '@supabase/ssr': specifier: 0.6.1 version: 0.6.1(@supabase/supabase-js@2.50.0) @@ -133,7 +133,7 @@ importers: version: 12.16.0(@emotion/is-prop-valid@1.2.2)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) geist: specifier: 1.4.2 - version: 1.4.2(next@15.3.3(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) + version: 1.4.2(next@15.3.3(@babel/core@7.27.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)) jaro-winkler: specifier: 0.2.8 version: 0.2.8 @@ -151,7 +151,7 @@ importers: version: 2.30.1 next: specifier: 15.3.3 - version: 15.3.3(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + version: 15.3.3(@babel/core@7.27.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) next-themes: specifier: 0.4.6 version: 0.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -200,43 +200,46 @@ importers: uuid: specifier: 11.1.0 version: 11.1.0 + vaul: + specifier: 1.1.2 + version: 1.1.2(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) zod: specifier: 3.25.56 version: 3.25.56 devDependencies: '@chromatic-com/storybook': - specifier: 4.0.0 - version: 4.0.0(storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3)) + specifier: 4.0.1 + version: 4.0.1(storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2)) '@playwright/test': specifier: 1.53.1 version: 1.53.1 '@storybook/addon-a11y': - specifier: 9.0.12 - version: 9.0.12(storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3)) + specifier: 9.0.14 + version: 9.0.14(storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2)) '@storybook/addon-docs': - specifier: 9.0.12 - version: 9.0.12(@types/react@18.3.17)(storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3)) + specifier: 9.0.14 + version: 9.0.14(@types/react@18.3.17)(storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2)) '@storybook/addon-links': - specifier: 9.0.12 - version: 9.0.12(react@18.3.1)(storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3)) + specifier: 9.0.14 + version: 9.0.14(react@18.3.1)(storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2)) '@storybook/addon-onboarding': - specifier: 9.0.12 - version: 9.0.12(storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3)) + specifier: 9.0.14 + version: 9.0.14(storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2)) '@storybook/nextjs': - specifier: 9.0.12 - version: 9.0.12(esbuild@0.25.5)(next@15.3.3(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3))(type-fest@4.41.0)(typescript@5.8.3)(webpack-hot-middleware@2.26.1)(webpack@5.99.9(esbuild@0.25.5)) + specifier: 9.0.14 + version: 9.0.14(esbuild@0.25.5)(next@15.3.3(@babel/core@7.27.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2))(type-fest@4.41.0)(typescript@5.8.3)(webpack-hot-middleware@2.26.1)(webpack@5.99.9(esbuild@0.25.5)) '@tanstack/eslint-plugin-query': - specifier: 5.78.0 - version: 5.78.0(eslint@8.57.1)(typescript@5.8.3) + specifier: 5.81.2 + version: 5.81.2(eslint@8.57.1)(typescript@5.8.3) '@tanstack/react-query-devtools': - specifier: 5.80.10 - version: 5.80.10(@tanstack/react-query@5.80.7(react@18.3.1))(react@18.3.1) + specifier: 5.81.5 + version: 5.81.5(@tanstack/react-query@5.80.7(react@18.3.1))(react@18.3.1) '@types/canvas-confetti': specifier: 1.9.0 version: 1.9.0 '@types/lodash': - specifier: 4.17.18 - version: 4.17.18 + specifier: 4.17.19 + version: 4.17.19 '@types/negotiator': specifier: 0.6.4 version: 0.6.4 @@ -259,8 +262,8 @@ importers: specifier: 11.25.2 version: 11.25.2 concurrently: - specifier: 9.1.2 - version: 9.1.2 + specifier: 9.2.0 + version: 9.2.0 eslint: specifier: 8.57.1 version: 8.57.1 @@ -268,8 +271,8 @@ importers: specifier: 15.3.4 version: 15.3.4(eslint@8.57.1)(typescript@5.8.3) eslint-plugin-storybook: - specifier: 9.0.12 - version: 9.0.12(eslint@8.57.1)(storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3))(typescript@5.8.3) + specifier: 9.0.14 + version: 9.0.14(eslint@8.57.1)(storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2))(typescript@5.8.3) import-in-the-middle: specifier: 1.14.2 version: 1.14.2 @@ -286,17 +289,17 @@ importers: specifier: 8.5.6 version: 8.5.6 prettier: - specifier: 3.5.3 - version: 3.5.3 + specifier: 3.6.2 + version: 3.6.2 prettier-plugin-tailwindcss: - specifier: 0.6.12 - version: 0.6.12(prettier@3.5.3) + specifier: 0.6.13 + version: 0.6.13(prettier@3.6.2) require-in-the-middle: specifier: 7.5.2 version: 7.5.2 storybook: - specifier: 9.0.12 - version: 9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3) + specifier: 9.0.14 + version: 9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2) tailwindcss: specifier: 3.4.17 version: 3.4.17 @@ -340,12 +343,12 @@ packages: resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.27.5': - resolution: {integrity: sha512-KiRAp/VoJaWkkte84TvUd9qjdbZAdiqyvMxrGl1N6vzFogKmaLgoM3L1kgtLicp2HP5fBJS8JrZKLVIZGVJAVg==} + '@babel/compat-data@7.27.7': + resolution: {integrity: sha512-xgu/ySj2mTiUFmdE9yCMfBxLp4DHd5DwmbbD05YAuICfodYT3VvRxbrh81LGQ/8UpSdtMdfKMn3KouYDX59DGQ==} engines: {node: '>=6.9.0'} - '@babel/core@7.27.4': - resolution: {integrity: sha512-bXYxrXFubeYdvB0NhD/NBB3Qi6aZeV20GOWVI47t2dkecCEoneR4NPVcb7abpXDEvejgrUfFtG6vG/zxAKmg+g==} + '@babel/core@7.27.7': + resolution: {integrity: sha512-BU2f9tlKQ5CAthiMIgpzAh4eDTLWo1mqi9jqE2OxMG0E/OM199VJt2q8BztTxpnSW0i1ymdwLXRJnYzvDM5r2w==} engines: {node: '>=6.9.0'} '@babel/generator@7.27.5': @@ -372,8 +375,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-define-polyfill-provider@0.6.4': - resolution: {integrity: sha512-jljfR1rGnXXNWnmQg2K3+bvhkxB51Rl32QRaOTuwwjviGrHzIbSc8+x9CpraDtbT7mfyjXObULP4w/adunNwAw==} + '@babel/helper-define-polyfill-provider@0.6.5': + resolution: {integrity: sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 @@ -435,8 +438,8 @@ packages: resolution: {integrity: sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==} engines: {node: '>=6.9.0'} - '@babel/parser@7.27.5': - resolution: {integrity: sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==} + '@babel/parser@7.27.7': + resolution: {integrity: sha512-qnzXzDXdr/po3bOTbTIQZ7+TxNKxpkN5IifVLXS+r7qwynkZfPyjZfE7hCXbo7IoO9TNcSyibgONsf2HauUd3Q==} engines: {node: '>=6.0.0'} hasBin: true @@ -558,8 +561,8 @@ packages: peerDependencies: '@babel/core': ^7.12.0 - '@babel/plugin-transform-classes@7.27.1': - resolution: {integrity: sha512-7iLhfFAubmpeJe/Wo2TVuDrykh/zlWXLzPNdL0Jqn/Xu8R3QQ8h9ff8FQoISZOsw74/HFqFI7NX63HN7QFIHKA==} + '@babel/plugin-transform-classes@7.27.7': + resolution: {integrity: sha512-CuLkokN1PEZ0Fsjtq+001aog/C2drDK9nTfK/NRK0n6rBin6cBrvM+zfQjDE+UllhR6/J4a6w8Xq9i4yi3mQrw==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -570,8 +573,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-destructuring@7.27.3': - resolution: {integrity: sha512-s4Jrok82JpiaIprtY2nHsYmrThKvvwgHwjgd7UMiYhZaN0asdXNLr0y+NjTfkA7SyQE5i2Fb7eawUOZmLvyqOA==} + '@babel/plugin-transform-destructuring@7.27.7': + resolution: {integrity: sha512-pg3ZLdIKWCP0CrJm0O4jYjVthyBeioVfvz9nwt6o5paUxsgJ/8GucSMAIaj6M7xA4WY+SrvtGu2LijzkdyecWQ==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -696,8 +699,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-object-rest-spread@7.27.3': - resolution: {integrity: sha512-7ZZtznF9g4l2JCImCo5LNKFHB5eXnN39lLtLY5Tg+VkR0jwOt7TBciMckuiQIOIW7L5tkQOCh3bVGYeXgMx52Q==} + '@babel/plugin-transform-object-rest-spread@7.27.7': + resolution: {integrity: sha512-201B1kFTWhckclcXpWHc8uUpYziDX/Pl4rxl0ZX0DiCZ3jknwfSUALL3QCYeeXXB37yWxJbo+g+Vfq8pAaHi3w==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -720,8 +723,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-transform-parameters@7.27.1': - resolution: {integrity: sha512-018KRk76HWKeZ5l4oTj2zPpSh+NbGdt0st5S6x0pga6HgrjBOJb24mMDHorFopOOd6YHkLgOZ+zaCjZGPO4aKg==} + '@babel/plugin-transform-parameters@7.27.7': + resolution: {integrity: sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -883,12 +886,12 @@ packages: resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.27.4': - resolution: {integrity: sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==} + '@babel/traverse@7.27.7': + resolution: {integrity: sha512-X6ZlfR/O/s5EQ/SnUSLzr+6kGnkg8HXGMzpgsMsrJVcfDtH1vIp6ctCN4eZ1LS5c0+te5Cb6Y514fASjMRJ1nw==} engines: {node: '>=6.9.0'} - '@babel/types@7.27.6': - resolution: {integrity: sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==} + '@babel/types@7.27.7': + resolution: {integrity: sha512-8OLQgDScAOHXnAz2cV+RfzzNMipuLVBz2biuAJFMV9bfkNf393je3VM8CLkjQodW5+iWsSJdSgSWT6rsZoXHPw==} engines: {node: '>=6.9.0'} '@bundled-es-modules/cookie@2.0.1': @@ -900,8 +903,8 @@ packages: '@bundled-es-modules/tough-cookie@0.1.6': resolution: {integrity: sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw==} - '@chromatic-com/storybook@4.0.0': - resolution: {integrity: sha512-FfyMHK/lz/dHezWxwNZv4ReFORWVvv+bJx71NT2BSfLhOKOaoZnKJOe4QLyGxWAB7ynnedrM9V9qea3FPFj+rQ==} + '@chromatic-com/storybook@4.0.1': + resolution: {integrity: sha512-GQXe5lyZl3yLewLJQyFXEpOp2h+mfN2bPrzYaOFNCJjO4Js9deKbRHTOSaiP2FRwZqDLdQwy2+SEGeXPZ94yYw==} engines: {node: '>=20.0.0', yarn: '>=1.22.18'} peerDependencies: storybook: ^0.0.0-0 || ^9.0.0 || ^9.1.0-0 @@ -1297,6 +1300,9 @@ packages: resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} + '@jridgewell/gen-mapping@0.3.10': + resolution: {integrity: sha512-HM2F4B9N4cA0RH2KQiIZOHAZqtP4xGS4IZ+SFe1SIbO4dyjf9MTY2Bo3vHYnm0hglWfXqBrzUBSa+cJfl3Xvrg==} + '@jridgewell/gen-mapping@0.3.8': resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} engines: {node: '>=6.0.0'} @@ -1309,15 +1315,21 @@ packages: resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} engines: {node: '>=6.0.0'} - '@jridgewell/source-map@0.3.6': - resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} + '@jridgewell/source-map@0.3.8': + resolution: {integrity: sha512-3EDAPd0B8X1gsQQgGHU8vyxSp2MB414z3roN67fY7nI0GV3GDthHfaWcbCfrC95tpAzA5xUvAuoO9Dxx/ywwRQ==} '@jridgewell/sourcemap-codec@1.5.0': resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + '@jridgewell/sourcemap-codec@1.5.2': + resolution: {integrity: sha512-gKYheCylLIedI+CSZoDtGkFV9YEBxRRVcfCH7OfAqh4TyUyRjEE6WVE/aXDXX0p8BIe/QgLcaAoI0220KRRFgg==} + '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/trace-mapping@0.3.27': + resolution: {integrity: sha512-VO95AxtSFMelbg3ouljAYnfvTEwSWVt/2YLf+U5Ejd8iT5mXE2Sa/1LGyvySMne2CGsepGLI7KpF3EzE3Aq9Mg==} + '@jsdevtools/ono@7.1.3': resolution: {integrity: sha512-4JQNk+3mVzK3xh2rqd6RB4J46qUR19azEHBneZyTZM+c456qOrbbM/5xcR8huNCCcbVt7+UmizG6GuUvPvKUYg==} @@ -1674,8 +1686,8 @@ packages: engines: {node: '>=18'} hasBin: true - '@pmmmwh/react-refresh-webpack-plugin@0.5.16': - resolution: {integrity: sha512-kLQc9xz6QIqd2oIYyXRUiAp79kGpFBm3fEM9ahfG1HI0WI5gdZ2OVHWdmZYnwODt7ISck+QuQ6sBPrtvUBML7Q==} + '@pmmmwh/react-refresh-webpack-plugin@0.5.17': + resolution: {integrity: sha512-tXDyE1/jzFsHXjhRZQ3hMl0IVhYe5qula43LDWIhVfjp9G/nT5OQY5AORVOrkEGAUltBJOfOWeETbmhm6kHhuQ==} engines: {node: '>= 10.13'} peerDependencies: '@types/webpack': 4.x || 5.x @@ -2546,48 +2558,48 @@ packages: resolution: {integrity: sha512-JZlVFE6/dYpP9tQmV0/ADfn32L9uFarHWxfcRhReKUnljz1ZiUM5zpX+PH8h5CJs6lao3TuFqnPm9IJJCEkE2w==} engines: {node: '>=10.8'} - '@storybook/addon-a11y@9.0.12': - resolution: {integrity: sha512-xdJPYNxYU6A3DA48h6y0o3XziCp4YDGXcFKkc5Ce1GPFCa7ebFFh2trHqzevoFSGdQxWc5M3W0A4dhQtkpT4Ww==} + '@storybook/addon-a11y@9.0.14': + resolution: {integrity: sha512-xDtzD89lyyq706yynJ8iAUjBfNebb7F5OoJXSAPYPnUiHoNHAcRT9ia2HrC6Yjp3f3JX2PRIEMjD5Myz3sL04A==} peerDependencies: - storybook: ^9.0.12 + storybook: ^9.0.14 - '@storybook/addon-docs@9.0.12': - resolution: {integrity: sha512-bAuFy4BWGEBIC0EAS4N+V8mHj7NZiSdDnJUSr4Al3znEVzNHLpQAMRznkga2Ok8x+gwcyBG7W47dLbDXVqLZDg==} + '@storybook/addon-docs@9.0.14': + resolution: {integrity: sha512-vjWH2FamLzoPZXitecbhRSUvQDj27q/dDaCKXSwCIwEVziIQrqHBGDmuJPCWoroCkKxLo8s8gwMi6wk5Minaqg==} peerDependencies: - storybook: ^9.0.12 + storybook: ^9.0.14 - '@storybook/addon-links@9.0.12': - resolution: {integrity: sha512-Ix7WXnCMkEWrgBrGCoZAFH276Xj/4g8e5Kao9BSZneUBjJJC2LuCaTftGeiM4kQ7sKBPOc/L6zIyusWckBvXIQ==} + '@storybook/addon-links@9.0.14': + resolution: {integrity: sha512-qzlRT+GRInP3H0bfvTVgdxWqbbSNLyBln9RNm1t5H3DsHc4NFyYdpPXXUapyFrWW7O0ByWMWO94RG0T+qG5R3g==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^9.0.12 + storybook: ^9.0.14 peerDependenciesMeta: react: optional: true - '@storybook/addon-onboarding@9.0.12': - resolution: {integrity: sha512-hqgaINYMDiA2op+Cb77LvwdJkgpMUMAnp5ugJjkn5icLpSTkZxnaQrlC0lTHOZBxUjR5NlS2ApSAuMvrCXQLAw==} + '@storybook/addon-onboarding@9.0.14': + resolution: {integrity: sha512-oJdRbOp8OkmDit8KpvkcOccN7Xczqznz55WH2oxaSLg0pWqn493BQGaRvq7Tn8qxTomNg9ibqr0rsHRZQKGlbQ==} peerDependencies: - storybook: ^9.0.12 + storybook: ^9.0.14 - '@storybook/builder-webpack5@9.0.12': - resolution: {integrity: sha512-zfmGYZjDppYPZZgSwW9ZRfIrCvshZcLombKmVEodlt/RMs5N5zaTCNf5p7+Z1BBcRpH5HXHWjwmrlobW6LzsLg==} + '@storybook/builder-webpack5@9.0.14': + resolution: {integrity: sha512-wu7OC+WE+PK7IaKKUEFN7a04CeSKJWkAqlgTV7BrrExHIbbOVTrmQSn/q02SYZJRebr4lllAD9cD90TlO8aV+g==} peerDependencies: - storybook: ^9.0.12 + storybook: ^9.0.14 typescript: '*' peerDependenciesMeta: typescript: optional: true - '@storybook/core-webpack@9.0.12': - resolution: {integrity: sha512-soFgpQh8pfba94YjkFBMNO4460/NEhdWe2WUPJs5drz2tOyGSElYma9KKjkbn+SQtr4qG0Xu7P56e8DJMXiMDg==} + '@storybook/core-webpack@9.0.14': + resolution: {integrity: sha512-LBWyCPKKBAb+Gb9QyIO2QcCf6QxRgzXfSyO1rnDqICrPdzukFtpEt4214v9cPw8wQqTclj7XADiOOsQMQjKmAA==} peerDependencies: - storybook: ^9.0.12 + storybook: ^9.0.14 - '@storybook/csf-plugin@9.0.12': - resolution: {integrity: sha512-5EueJQJAu77Lh+EedG4Q/kEOZNlTY/g+fWsT7B5DTtLVy0ypnghsHY8X3KYT/0+NNgTtoO0if4F+ejVYaLnMzA==} + '@storybook/csf-plugin@9.0.14': + resolution: {integrity: sha512-PKUmF5y/SfPOifC2bRo79YwfGv6TYISM5JK6r6FHVKMwV1nWLmj7Xx2t5aHa/5JggdBz/iGganTP7oo7QOn+0A==} peerDependencies: - storybook: ^9.0.12 + storybook: ^9.0.14 '@storybook/global@5.0.0': resolution: {integrity: sha512-FcOqPAXACP0I3oJ/ws6/rrPT9WGhu915Cg8D02a9YxLo0DE9zI+a9A5gRGvmQ09fiWPukqI8ZAEoQEdWUKMQdQ==} @@ -2599,14 +2611,14 @@ packages: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - '@storybook/nextjs@9.0.12': - resolution: {integrity: sha512-7+toUNcn17S1MlJTMj3miZ3ev8WIeWcr/2OVbwzFp+BKyZVVOXd5+YSWBqwg+k4H5CmwTrFYegQ3Yp2lqzgKoA==} + '@storybook/nextjs@9.0.14': + resolution: {integrity: sha512-WZ1hExxB+XcbAsNDSpDyvqncoJGqoudsXrZoQuU9YXSOfddLVIVvDa9N7/q6d5UPKwAQwm2tuTup41Wy0octIg==} engines: {node: '>=20.0.0'} peerDependencies: next: ^14.1.0 || ^15.0.0 react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^9.0.12 + storybook: ^9.0.14 typescript: '*' webpack: ^5.0.0 peerDependenciesMeta: @@ -2615,13 +2627,13 @@ packages: webpack: optional: true - '@storybook/preset-react-webpack@9.0.12': - resolution: {integrity: sha512-Tl3Qrll29dwltI4lP15zbeSXqikKa4Ucp6NKf4+W/4adzOqGRjj/bIzO3FsIPoGNs4wfy5DsOgUUPHxI4Jx97w==} + '@storybook/preset-react-webpack@9.0.14': + resolution: {integrity: sha512-T+djeLW4lHslFRhnlyVUcLVRMc2+tkmLHiv0ATzec/IipbMxHrZ5U5feWX6RuRsux6A00VWoG9Wdda59AJ7qwg==} engines: {node: '>=20.0.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^9.0.12 + storybook: ^9.0.14 typescript: '*' peerDependenciesMeta: typescript: @@ -2633,20 +2645,20 @@ packages: typescript: '>= 4.x' webpack: '>= 4' - '@storybook/react-dom-shim@9.0.12': - resolution: {integrity: sha512-OMBitzkJRga/UJF1ScSnaxgBSlAVePCK8wzPkGDn0MmsjZ4oDWuNZeKnVO1+tb6n2rZHws7RmKGxHzHAZTY+zQ==} + '@storybook/react-dom-shim@9.0.14': + resolution: {integrity: sha512-fXMzhgFMnGZUhWm9zWiR8qOB90OykPhkB/qiebFbD/wUedPyp3H1+NAzX1/UWV2SYqr+aFK9vH1PokAYbpTRsw==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^9.0.12 + storybook: ^9.0.14 - '@storybook/react@9.0.12': - resolution: {integrity: sha512-rDrf5MDfsguNDTSOfGqhAjQDhp3jDMdzAoCqLjQ75M647C8nsv9i+fftO3k0rMxIJRrESpZWqVZ4tsjOX+J3DA==} + '@storybook/react@9.0.14': + resolution: {integrity: sha512-Ig4Y1xUOMcOWtQ/H73JZa4MeE0GJvYOcK16AhbfvPZMotdXCFyPbb1/pWhS209HuGwfNTVvWGz9rk7KrHmKsNw==} engines: {node: '>=20.0.0'} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0-beta - storybook: ^9.0.12 + storybook: ^9.0.14 typescript: '>= 4.9.x' peerDependenciesMeta: typescript: @@ -2685,21 +2697,21 @@ packages: '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} - '@tanstack/eslint-plugin-query@5.78.0': - resolution: {integrity: sha512-hYkhWr3UP0CkAsn/phBVR98UQawbw8CmTSgWtdgEBUjI60/GBaEIkpgi/Bp/2I8eIDK4+vdY7ac6jZx+GR+hEQ==} + '@tanstack/eslint-plugin-query@5.81.2': + resolution: {integrity: sha512-h4k6P6fm5VhKP5NkK+0TTVpGGyKQdx6tk7NYYG7J7PkSu7ClpLgBihw7yzK8N3n5zPaF3IMyErxfoNiXWH/3/A==} peerDependencies: eslint: ^8.57.0 || ^9.0.0 '@tanstack/query-core@5.80.7': resolution: {integrity: sha512-s09l5zeUKC8q7DCCCIkVSns8zZrK4ZDT6ryEjxNBFi68G4z2EBobBS7rdOY3r6W1WbUDpc1fe5oY+YO/+2UVUg==} - '@tanstack/query-devtools@5.80.0': - resolution: {integrity: sha512-D6gH4asyjaoXrCOt5vG5Og/YSj0D/TxwNQgtLJIgWbhbWCC/emu2E92EFoVHh4ppVWg1qT2gKHvKyQBEFZhCuA==} + '@tanstack/query-devtools@5.81.2': + resolution: {integrity: sha512-jCeJcDCwKfoyyBXjXe9+Lo8aTkavygHHsUHAlxQKKaDeyT0qyQNLKl7+UyqYH2dDF6UN/14873IPBHchcsU+Zg==} - '@tanstack/react-query-devtools@5.80.10': - resolution: {integrity: sha512-6JL63fSc7kxyGOLV2w466SxhMn/m7LZk/ximQciy6OpVt+n2A8Mq3S0QwhIzfm4WEwLK/F3OELfzRToQburnYA==} + '@tanstack/react-query-devtools@5.81.5': + resolution: {integrity: sha512-lCGMu4RX0uGnlrlLeSckBfnW/UV+KMlTBVqa97cwK7Z2ED5JKnZRSjNXwoma6sQBTJrcULvzgx2K6jEPvNUpDw==} peerDependencies: - '@tanstack/react-query': ^5.80.10 + '@tanstack/react-query': ^5.81.5 react: ^18 || ^19 '@tanstack/react-query@5.80.7': @@ -2753,6 +2765,9 @@ packages: '@types/canvas-confetti@1.9.0': resolution: {integrity: sha512-aBGj/dULrimR1XDZLtG9JwxX1b4HPRF6CX9Yfwh3NvstZEm1ZL7RBnel4keCPSqs1ANRu1u2Aoz9R+VmtjYuTg==} + '@types/chai@5.2.2': + resolution: {integrity: sha512-8kB30R7Hwqf40JPiKhVzodJs2Qc1ZJ5zuT3uzw5Hq/dhNCl3G3l83jfpdI1e20BP348+fV7VIL/+FxaXkqBmWg==} + '@types/connect@3.4.38': resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} @@ -2801,6 +2816,9 @@ packages: '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + '@types/doctrine@0.0.9': resolution: {integrity: sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA==} @@ -2840,8 +2858,8 @@ packages: '@types/junit-report-builder@3.0.2': resolution: {integrity: sha512-R5M+SYhMbwBeQcNXYWNCZkl09vkVfAtcPIaCGdzIkkbeaTrVbGQ7HVgi4s+EmM/M1K4ZuWQH0jGcvMvNePfxYA==} - '@types/lodash@4.17.18': - resolution: {integrity: sha512-KJ65INaxqxmU6EoCiJmRPZC9H9RVWCRd349tXM2M3O5NA7cY6YL7c0bHAHQ93NOfTObEQ004kd2QVHs/r0+m4g==} + '@types/lodash@4.17.19': + resolution: {integrity: sha512-NYqRyg/hIQrYPT9lbOeYc3kIRabJDn/k4qQHIXUpx88CBDww2fD15Sg5kbXlW86zm2XEW4g0QxkTI3/Kfkc7xQ==} '@types/mdast@4.0.4': resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} @@ -2941,16 +2959,48 @@ packages: peerDependencies: typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/project-service@8.35.0': + resolution: {integrity: sha512-41xatqRwWZuhUMF/aZm2fcUsOFKNcG28xqRSS6ZVr9BVJtGExosLAm5A1OxTjRMagx8nJqva+P5zNIGt8RIgbQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/project-service@8.35.1': + resolution: {integrity: sha512-VYxn/5LOpVxADAuP3NrnxxHYfzVtQzLKeldIhDhzC8UHaiQvYlXvKuVho1qLduFbJjjy5U5bkGwa3rUGUb1Q6Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/scope-manager@8.34.1': resolution: {integrity: sha512-beu6o6QY4hJAgL1E8RaXNC071G4Kso2MGmJskCFQhRhg8VOH/FDbC8soP8NHN7e/Hdphwp8G8cE6OBzC8o41ZA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/scope-manager@8.35.0': + resolution: {integrity: sha512-+AgL5+mcoLxl1vGjwNfiWq5fLDZM1TmTPYs2UkyHfFhgERxBbqHlNjRzhThJqz+ktBqTChRYY6zwbMwy0591AA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/scope-manager@8.35.1': + resolution: {integrity: sha512-s/Bpd4i7ht2934nG+UoSPlYXd08KYz3bmjLEb7Ye1UVob0d1ENiT3lY8bsCmik4RqfSbPw9xJJHbugpPpP5JUg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/tsconfig-utils@8.34.1': resolution: {integrity: sha512-K4Sjdo4/xF9NEeA2khOb7Y5nY6NSXBnod87uniVYW9kHP+hNlDV8trUSFeynA2uxWam4gIWgWoygPrv9VMWrYg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/tsconfig-utils@8.35.0': + resolution: {integrity: sha512-04k/7247kZzFraweuEirmvUj+W3bJLI9fX6fbo1Qm2YykuBvEhRTPl8tcxlYO8kZZW+HIXfkZNoasVb8EV4jpA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/tsconfig-utils@8.35.1': + resolution: {integrity: sha512-K5/U9VmT9dTHoNowWZpz+/TObS3xqC5h0xAIjXPw+MNcKV9qg6eSatEnmeAwkjHijhACH0/N7bkhKvbt1+DXWQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/type-utils@8.34.1': resolution: {integrity: sha512-Tv7tCCr6e5m8hP4+xFugcrwTOucB8lshffJ6zf1mF1TbU67R+ntCc6DzLNKM+s/uzDyv8gLq7tufaAhIBYeV8g==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2962,12 +3012,32 @@ packages: resolution: {integrity: sha512-rjLVbmE7HR18kDsjNIZQHxmv9RZwlgzavryL5Lnj2ujIRTeXlKtILHgRNmQ3j4daw7zd+mQgy+uyt6Zo6I0IGA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/types@8.35.0': + resolution: {integrity: sha512-0mYH3emanku0vHw2aRLNGqe7EXh9WHEhi7kZzscrMDf6IIRUQ5Jk4wp1QrledE/36KtdZrVfKnE32eZCf/vaVQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/types@8.35.1': + resolution: {integrity: sha512-q/O04vVnKHfrrhNAscndAn1tuQhIkwqnaW+eu5waD5IPts2eX1dgJxgqcPx5BX109/qAz7IG6VrEPTOYKCNfRQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/typescript-estree@8.34.1': resolution: {integrity: sha512-rjCNqqYPuMUF5ODD+hWBNmOitjBWghkGKJg6hiCHzUvXRy6rK22Jd3rwbP2Xi+R7oYVvIKhokHVhH41BxPV5mA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/typescript-estree@8.35.0': + resolution: {integrity: sha512-F+BhnaBemgu1Qf8oHrxyw14wq6vbL8xwWKKMwTMwYIRmFFY/1n/9T/jpbobZL8vp7QyEUcC6xGrnAO4ua8Kp7w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/typescript-estree@8.35.1': + resolution: {integrity: sha512-Vvpuvj4tBxIka7cPs6Y1uvM7gJgdF5Uu9F+mBJBPY4MhvjrjWGK4H0lVgLJd/8PWZ23FTqsaJaLEkBCFUk8Y9g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/utils@8.34.1': resolution: {integrity: sha512-mqOwUdZ3KjtGk7xJJnLbHxTuWVn3GO2WZZuM+Slhkun4+qthLdXx32C8xIXbO1kfCECb3jIs3eoxK3eryk7aoQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2975,10 +3045,32 @@ packages: eslint: ^8.57.0 || ^9.0.0 typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/utils@8.35.0': + resolution: {integrity: sha512-nqoMu7WWM7ki5tPgLVsmPM8CkqtoPUG6xXGeefM5t4x3XumOEKMoUZPdi+7F+/EotukN4R9OWdmDxN80fqoZeg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/utils@8.35.1': + resolution: {integrity: sha512-lhnwatFmOFcazAsUm3ZnZFpXSxiwoa1Lj50HphnDe1Et01NF4+hrdXONSUHIcbVu2eFb1bAf+5yjXkGVkXBKAQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + '@typescript-eslint/visitor-keys@8.34.1': resolution: {integrity: sha512-xoh5rJ+tgsRKoXnkBPFRLZ7rjKM0AfVbC68UZ/ECXoDbfggb9RbEySN359acY1vS3qZ0jVTVWzbtfapwm5ztxw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@typescript-eslint/visitor-keys@8.35.0': + resolution: {integrity: sha512-zTh2+1Y8ZpmeQaQVIc/ZZxsx8UzgKJyNg1PTvjzC7WMhPSVS8bfDX34k1SrwOf016qd5RU3az2UxUNue3IfQ5g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/visitor-keys@8.35.1': + resolution: {integrity: sha512-VRwixir4zBWCSTP/ljEo091lbpypz57PoeAQ9imjG+vbeof9LplljsL1mos4ccG6H9IjfrVGM359RozUnuFhpw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@ungap/structured-clone@1.3.0': resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} @@ -3077,17 +3169,17 @@ packages: cpu: [x64] os: [win32] - '@vitest/expect@3.0.9': - resolution: {integrity: sha512-5eCqRItYgIML7NNVgJj6TVCmdzE7ZVgJhruW0ziSQV4V7PvLkDL1bBkBdcTs/VuIz0IxPb5da1IDSqc1TR9eig==} + '@vitest/expect@3.2.4': + resolution: {integrity: sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==} - '@vitest/pretty-format@3.0.9': - resolution: {integrity: sha512-OW9F8t2J3AwFEwENg3yMyKWweF7oRJlMyHOMIhO5F3n0+cgQAJZBjNgrF8dLwFTEXl5jUqBLXd9QyyKv8zEcmA==} + '@vitest/pretty-format@3.2.4': + resolution: {integrity: sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==} - '@vitest/spy@3.0.9': - resolution: {integrity: sha512-/CcK2UDl0aQ2wtkp3YVWldrpLRNCfVcIOFGlVGKO4R5eajsH393Z1yiXLVQ7vWsj26JOEjeZI0x5sm5P4OGUNQ==} + '@vitest/spy@3.2.4': + resolution: {integrity: sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==} - '@vitest/utils@3.0.9': - resolution: {integrity: sha512-ilHM5fHhZ89MCp5aAaM9uhfl1c2JdxVxl3McqsdVyVNN6JffnEen8UMCdRTzOhGXNQGo5GNL9QugHrz727Wnng==} + '@vitest/utils@3.2.4': + resolution: {integrity: sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==} '@webassemblyjs/ast@1.14.1': resolution: {integrity: sha512-nuBEDgQfm1ccRp/8bCQrx1frohyufl4JlbMMZ4P1wpeOfDhF6FQkxZJ1b/e+PLwr6X1Nhw6OLme5usuBWYBvuQ==} @@ -3366,8 +3458,8 @@ packages: '@babel/core': ^7.12.0 webpack: '>=5' - babel-plugin-polyfill-corejs2@0.4.13: - resolution: {integrity: sha512-3sX/eOms8kd3q2KZ6DAhKPc0dgm525Gqq5NtWKZ7QYYZEv57OQ54KtblzJzH1lQF/eQxO8KjWGIK9IPUJNus5g==} + babel-plugin-polyfill-corejs2@0.4.14: + resolution: {integrity: sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 @@ -3376,8 +3468,8 @@ packages: peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 - babel-plugin-polyfill-regenerator@0.6.4: - resolution: {integrity: sha512-7gD3pRadPrbjhjLyxebmx/WrFYcuSjZ0XbdUujQMZ/fcE9oeewk2U/7PCvez84UeuK3oSjmPZ0Ch0dlupQvGzw==} + babel-plugin-polyfill-regenerator@0.6.5: + resolution: {integrity: sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==} peerDependencies: '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0 @@ -3446,8 +3538,8 @@ packages: browserify-zlib@0.2.0: resolution: {integrity: sha512-Z942RysHXmJrhqk88FmKBVq/v5tqmSkDz7p54G/MGyjMnCFFnC79XWNbg+Vta8W6Wb2qtSZTSxIGkJrRpCFEiA==} - browserslist@4.25.0: - resolution: {integrity: sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==} + browserslist@4.25.1: + resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -3503,6 +3595,9 @@ packages: caniuse-lite@1.0.30001723: resolution: {integrity: sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==} + caniuse-lite@1.0.30001726: + resolution: {integrity: sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==} + case-sensitive-paths-webpack-plugin@2.4.0: resolution: {integrity: sha512-roIFONhcxog0JSSWbvVAh3OocukmSgpqOH6YpMkCvav/ySIV3JKg4Dc8vYtQjYi/UxpNE36r/9v+VqTQqgkYmw==} engines: {node: '>=4'} @@ -3655,8 +3750,8 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - concurrently@9.1.2: - resolution: {integrity: sha512-H9MWcoPsYddwbOGM6difjVwVZHl63nwMEwDJG/L7VGtuaJhb12h2caPG2tVPWs7emuYix252iGfqOyrz1GczTQ==} + concurrently@9.2.0: + resolution: {integrity: sha512-IsB/fiXTupmagMW4MNp2lx2cdSN2FfZq78vF90LBB+zZHArbIQZjQtzXCiXnvTxCZSvXanTqFLWBjw2UkLx1SQ==} engines: {node: '>=18'} hasBin: true @@ -3705,6 +3800,9 @@ packages: create-ecdh@4.0.4: resolution: {integrity: sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==} + create-hash@1.1.3: + resolution: {integrity: sha512-snRpch/kwQhcdlnZKYanNF1m0RDlrCdSKQaH87w1FCFPVPNCQ/Il9QJKAX2jVBZddRdaHBMC+zXa9Gw9tmkNUA==} + create-hash@1.2.0: resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} @@ -3741,8 +3839,8 @@ packages: css-to-react-native@3.2.0: resolution: {integrity: sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==} - css-what@6.1.0: - resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + css-what@6.2.2: + resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==} engines: {node: '>= 6'} css.escape@1.5.1: @@ -3981,8 +4079,8 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - electron-to-chromium@1.5.169: - resolution: {integrity: sha512-q7SQx6mkLy0GTJK9K9OiWeaBMV4XQtBSdf6MJUzDB/H/5tFXfIiX38Lci1Kl6SsgiEhz1SQI1ejEOU5asWEhwQ==} + electron-to-chromium@1.5.177: + resolution: {integrity: sha512-7EH2G59nLsEMj97fpDuvVcYi6lwTcM1xuWw3PssD8xzboAW7zj7iB3COEEEATUfjLHrs5uKBLQT03V/8URx06g==} elliptic@6.6.1: resolution: {integrity: sha512-RaddvvMatK2LJHqFJ+YA4WysVN5Ita9E35botqIYspQ4TkRAlCicdzKOjlyv/1Za5RyTNn7di//eEV0uTAfe3g==} @@ -4013,8 +4111,8 @@ packages: endent@2.1.0: resolution: {integrity: sha512-r8VyPX7XL8U01Xgnb1CjZ3XV+z90cXIJ9JPE/R9SEC9vpw2P6CfsRPJmp20DppC5N7ZAMCmjYkJIa744Iyg96w==} - enhanced-resolve@5.18.1: - resolution: {integrity: sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==} + enhanced-resolve@5.18.2: + resolution: {integrity: sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==} engines: {node: '>=10.13.0'} enquirer@2.4.1: @@ -4172,12 +4270,12 @@ packages: peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 - eslint-plugin-storybook@9.0.12: - resolution: {integrity: sha512-dSzcozoI7tQRqfMODWfxahrRmKWsK88yKSUcO0+s361oYcX7nf8nEu99TQ/wuDLRHh+Zi7E2j43cPMH8gFo8OA==} + eslint-plugin-storybook@9.0.14: + resolution: {integrity: sha512-YZsDhyFgVfeFPdvd7Xcl9ZusY7Jniq7AOAWN/cdg0a2Y+ywKKNYrQ+EfyuhXsiMjh58plYKMpJYxKVxeUwW9jw==} engines: {node: '>=20.0.0'} peerDependencies: eslint: '>=8' - storybook: ^9.0.12 + storybook: ^9.0.14 eslint-scope@5.1.1: resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==} @@ -4532,6 +4630,9 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} + hash-base@2.0.2: + resolution: {integrity: sha512-0TROgQ1/SxE6KmxWSvXHvRj90/Xo1JvZShofnYF+f6ZsGtR4eES7WfrQzPalmyagfKZCXpVnitiRebZulWsbiw==} + hash-base@3.0.5: resolution: {integrity: sha512-vXm0l45VbcHEVlTCzs8M+s0VeYsB2lnlAaThoLKGXr3bE/VWDOelNUnycUPEhKEaXARL2TEFjBOyUiM6+55KBg==} engines: {node: '>= 0.10'} @@ -5551,12 +5652,12 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} - pathval@2.0.0: - resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} engines: {node: '>= 14.16'} - pbkdf2@3.1.2: - resolution: {integrity: sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==} + pbkdf2@3.1.3: + resolution: {integrity: sha512-wfRLBZ0feWRhCIkoMB6ete7czJcnNnqRpcoWQBLqatqXXmelSRqfdDK4F3u9T2s2cXas/hQJcryI/4lAL+XTlA==} engines: {node: '>=0.12'} pg-int8@1.0.1: @@ -5725,8 +5826,8 @@ packages: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier-plugin-tailwindcss@0.6.12: - resolution: {integrity: sha512-OuTQKoqNwV7RnxTPwXWzOFXy6Jc4z8oeRZYGuMpRyG3WbuR3jjXdQFK8qFBMBx8UHWdHrddARz2fgUenild6aw==} + prettier-plugin-tailwindcss@0.6.13: + resolution: {integrity: sha512-uQ0asli1+ic8xrrSmIOaElDu0FacR4x69GynTh2oZjFY10JUt6EEumTQl5tB4fMeD6I1naKd+4rXQQ7esT2i1g==} engines: {node: '>=14.21.3'} peerDependencies: '@ianvs/prettier-plugin-sort-imports': '*' @@ -5780,8 +5881,8 @@ packages: prettier-plugin-svelte: optional: true - prettier@3.5.3: - resolution: {integrity: sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==} + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} engines: {node: '>=14'} hasBin: true @@ -6104,6 +6205,9 @@ packages: deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true + ripemd160@2.0.1: + resolution: {integrity: sha512-J7f4wutN8mdbV08MJnXibYpCOPHR+yzy+iQ/AsjMv2j8cLavQ8VGagDFUwwTAdF8FmRKVeNpbTTEwNHCW1g94w==} + ripemd160@2.0.2: resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} @@ -6316,8 +6420,8 @@ packages: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} - storybook@9.0.12: - resolution: {integrity: sha512-mpACe6BMd/M5sqcOiA8NmWIm2zdx0t4ujnA4NTcq4aErdK/KKuU255UM4pO3DIf5zWR5VrDfNV5UaMi/VgE2mA==} + storybook@9.0.14: + resolution: {integrity: sha512-PfVo9kSa4XsDTD2gXFvMRGix032+clBDcUMI4MhUzYxONLiZifnhwch4p/1lG+c3IVN4qkOEgGNc9PEgVMgApw==} hasBin: true peerDependencies: prettier: ^2 || ^3 @@ -6512,8 +6616,8 @@ packages: uglify-js: optional: true - terser@5.42.0: - resolution: {integrity: sha512-UYCvU9YQW2f/Vwl+P0GfhxJxbUGLwd+5QrrGgLajzWAtC/23AX0vcise32kkP7Eu0Wu9VlzzHAXkLObgjQfFlQ==} + terser@5.43.1: + resolution: {integrity: sha512-+6erLbBm0+LROX2sPXlUYx/ux5PyE9K/a92Wrt6oA+WDAoFTdpHE5tCYCI5PNzq2y8df4rA+QgHLJuR4jNymsg==} engines: {node: '>=10'} hasBin: true @@ -6545,10 +6649,14 @@ packages: resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} engines: {node: '>=14.0.0'} - tinyspy@3.0.2: - resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} + tinyspy@4.0.3: + resolution: {integrity: sha512-t2T/WLB2WRgZ9EpE4jgPJ9w+i66UZfDc8wHh0xrwiRNN+UwH98GIJkTeZqX9rg0i0ptwzqW+uYeIF0T4F8LR7A==} engines: {node: '>=14.0.0'} + to-buffer@1.2.1: + resolution: {integrity: sha512-tB82LpAIWjhLYbqjx3X4zEeHN6M8CiuOEy2JY8SEQVdYRe3CCHOFaqrBW1doLDrfpWhplcW7BL+bO3/6S3pcDQ==} + engines: {node: '>= 0.4'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -6813,6 +6921,12 @@ packages: resolution: {integrity: sha512-BgWVbCI72aIQy937xbawcs+hrVaN/CZ2UwutgaJ36hGqRrLNM+f5LUT/YPRbo8IV/ASeFzXszezV+y2+rq3l8A==} engines: {node: '>= 0.10'} + vaul@1.1.2: + resolution: {integrity: sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + vfile-message@4.0.2: resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} @@ -6851,6 +6965,10 @@ packages: resolution: {integrity: sha512-ykKKus8lqlgXX/1WjudpIEjqsafjOTcOJqxnAbMLAu/KCsDCJ6GBtvscewvTkrn24HsnvFwrSCbenFrhtcCsAA==} engines: {node: '>=10.13.0'} + webpack-sources@3.3.3: + resolution: {integrity: sha512-yd1RBzSGanHkitROoPFd6qsrxt+oFhg/129YzheDGqeustzX0vTZJZsSsQjVQC4yzBQ56K55XU8gaNCtIzOnTg==} + engines: {node: '>=10.13.0'} + webpack-virtual-modules@0.5.0: resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==} @@ -6910,8 +7028,8 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@8.18.2: - resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==} + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -6995,8 +7113,8 @@ snapshots: '@ampproject/remapping@2.3.0': dependencies: - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/gen-mapping': 0.3.10 + '@jridgewell/trace-mapping': 0.3.27 '@apidevtools/json-schema-ref-parser@11.7.2': dependencies: @@ -7029,20 +7147,20 @@ snapshots: js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.27.5': {} + '@babel/compat-data@7.27.7': {} - '@babel/core@7.27.4': + '@babel/core@7.27.7': dependencies: '@ampproject/remapping': 2.3.0 '@babel/code-frame': 7.27.1 '@babel/generator': 7.27.5 '@babel/helper-compilation-targets': 7.27.2 - '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.4) + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.7) '@babel/helpers': 7.27.6 - '@babel/parser': 7.27.5 + '@babel/parser': 7.27.7 '@babel/template': 7.27.2 - '@babel/traverse': 7.27.4 - '@babel/types': 7.27.6 + '@babel/traverse': 7.27.7 + '@babel/types': 7.27.7 convert-source-map: 2.0.0 debug: 4.4.1 gensync: 1.0.0-beta.2 @@ -7053,47 +7171,47 @@ snapshots: '@babel/generator@7.27.5': dependencies: - '@babel/parser': 7.27.5 - '@babel/types': 7.27.6 - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 + '@babel/parser': 7.27.7 + '@babel/types': 7.27.7 + '@jridgewell/gen-mapping': 0.3.10 + '@jridgewell/trace-mapping': 0.3.27 jsesc: 3.1.0 '@babel/helper-annotate-as-pure@7.27.3': dependencies: - '@babel/types': 7.27.6 + '@babel/types': 7.27.7 '@babel/helper-compilation-targets@7.27.2': dependencies: - '@babel/compat-data': 7.27.5 + '@babel/compat-data': 7.27.7 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.25.0 + browserslist: 4.25.1 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-create-class-features-plugin@7.27.1(@babel/core@7.27.4)': + '@babel/helper-create-class-features-plugin@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-member-expression-to-functions': 7.27.1 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/helper-replace-supers': 7.27.1(@babel/core@7.27.4) + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.27.7) '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/traverse': 7.27.4 + '@babel/traverse': 7.27.7 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/helper-create-regexp-features-plugin@7.27.1(@babel/core@7.27.4)': + '@babel/helper-create-regexp-features-plugin@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-annotate-as-pure': 7.27.3 regexpu-core: 6.2.0 semver: 6.3.1 - '@babel/helper-define-polyfill-provider@0.6.4(@babel/core@7.27.4)': + '@babel/helper-define-polyfill-provider@0.6.5(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 debug: 4.4.1 @@ -7104,55 +7222,55 @@ snapshots: '@babel/helper-member-expression-to-functions@7.27.1': dependencies: - '@babel/traverse': 7.27.4 - '@babel/types': 7.27.6 + '@babel/traverse': 7.27.7 + '@babel/types': 7.27.7 transitivePeerDependencies: - supports-color '@babel/helper-module-imports@7.27.1': dependencies: - '@babel/traverse': 7.27.4 - '@babel/types': 7.27.6 + '@babel/traverse': 7.27.7 + '@babel/types': 7.27.7 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.27.3(@babel/core@7.27.4)': + '@babel/helper-module-transforms@7.27.3(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-module-imports': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.27.4 + '@babel/traverse': 7.27.7 transitivePeerDependencies: - supports-color '@babel/helper-optimise-call-expression@7.27.1': dependencies: - '@babel/types': 7.27.6 + '@babel/types': 7.27.7 '@babel/helper-plugin-utils@7.27.1': {} - '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.27.4)': + '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-wrap-function': 7.27.1 - '@babel/traverse': 7.27.4 + '@babel/traverse': 7.27.7 transitivePeerDependencies: - supports-color - '@babel/helper-replace-supers@7.27.1(@babel/core@7.27.4)': + '@babel/helper-replace-supers@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-member-expression-to-functions': 7.27.1 '@babel/helper-optimise-call-expression': 7.27.1 - '@babel/traverse': 7.27.4 + '@babel/traverse': 7.27.7 transitivePeerDependencies: - supports-color '@babel/helper-skip-transparent-expression-wrappers@7.27.1': dependencies: - '@babel/traverse': 7.27.4 - '@babel/types': 7.27.6 + '@babel/traverse': 7.27.7 + '@babel/types': 7.27.7 transitivePeerDependencies: - supports-color @@ -7165,568 +7283,574 @@ snapshots: '@babel/helper-wrap-function@7.27.1': dependencies: '@babel/template': 7.27.2 - '@babel/traverse': 7.27.4 - '@babel/types': 7.27.6 + '@babel/traverse': 7.27.7 + '@babel/types': 7.27.7 transitivePeerDependencies: - supports-color '@babel/helpers@7.27.6': dependencies: '@babel/template': 7.27.2 - '@babel/types': 7.27.6 + '@babel/types': 7.27.7 - '@babel/parser@7.27.5': + '@babel/parser@7.27.7': dependencies: - '@babel/types': 7.27.6 + '@babel/types': 7.27.7 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.27.4 + '@babel/traverse': 7.27.7 transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.27.7) transitivePeerDependencies: - supports-color - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.27.4 + '@babel/traverse': 7.27.7 transitivePeerDependencies: - supports-color - '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.27.4)': + '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.27.4)': + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.27.4)': + '@babel/plugin-syntax-dynamic-import@7.8.3(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-assertions@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-syntax-import-assertions@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.27.4)': + '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.4) + '@babel/core': 7.27.7 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.7) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-async-generator-functions@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-async-generator-functions@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.27.4) - '@babel/traverse': 7.27.4 + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.27.7) + '@babel/traverse': 7.27.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-async-to-generator@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-async-to-generator@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-module-imports': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.27.4) + '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.27.7) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-block-scoping@7.27.5(@babel/core@7.27.4)': + '@babel/plugin-transform-block-scoping@7.27.5(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-class-properties@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-class-properties@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 - '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.4) + '@babel/core': 7.27.7 + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.7) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-class-static-block@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-class-static-block@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 - '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.4) + '@babel/core': 7.27.7 + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.7) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-classes@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-classes@7.27.7(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-replace-supers': 7.27.1(@babel/core@7.27.4) - '@babel/traverse': 7.27.4 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.27.7) + '@babel/traverse': 7.27.7 globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-computed-properties@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-computed-properties@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 '@babel/template': 7.27.2 - '@babel/plugin-transform-destructuring@7.27.3(@babel/core@7.27.4)': + '@babel/plugin-transform-destructuring@7.27.7(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/traverse': 7.27.7 + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-dotall-regex@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.7) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-dotall-regex@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.4) + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.7) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.4) + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-exponentiation-operator@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-exponentiation-operator@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 - '@babel/helper-plugin-utils': 7.27.1 - - '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.27.4)': - dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 - '@babel/traverse': 7.27.4 + '@babel/traverse': 7.27.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-json-strings@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-json-strings@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-literals@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-literals@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-logical-assignment-operators@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-logical-assignment-operators@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 - '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.4) + '@babel/core': 7.27.7 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.7) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 - '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.4) + '@babel/core': 7.27.7 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.7) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-systemjs@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-modules-systemjs@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 - '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.4) + '@babel/core': 7.27.7 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.7) '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@babel/traverse': 7.27.4 + '@babel/traverse': 7.27.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 - '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.4) + '@babel/core': 7.27.7 + '@babel/helper-module-transforms': 7.27.3(@babel/core@7.27.7) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-named-capturing-groups-regex@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-named-capturing-groups-regex@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.4) + '@babel/core': 7.27.7 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.7) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-nullish-coalescing-operator@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-nullish-coalescing-operator@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-numeric-separator@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-numeric-separator@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-object-rest-spread@7.27.3(@babel/core@7.27.4)': + '@babel/plugin-transform-object-rest-spread@7.27.7(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-destructuring': 7.27.3(@babel/core@7.27.4) - '@babel/plugin-transform-parameters': 7.27.1(@babel/core@7.27.4) - - '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.27.4)': - dependencies: - '@babel/core': 7.27.4 - '@babel/helper-plugin-utils': 7.27.1 - '@babel/helper-replace-supers': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-destructuring': 7.27.7(@babel/core@7.27.7) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.27.7) + '@babel/traverse': 7.27.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-optional-catch-binding@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 + '@babel/helper-plugin-utils': 7.27.1 + '@babel/helper-replace-supers': 7.27.1(@babel/core@7.27.7) + transitivePeerDependencies: + - supports-color + + '@babel/plugin-transform-optional-catch-binding@7.27.1(@babel/core@7.27.7)': + dependencies: + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-optional-chaining@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-optional-chaining@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-parameters@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-private-methods@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-private-methods@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 - '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.4) + '@babel/core': 7.27.7 + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.7) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-private-property-in-object@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-private-property-in-object@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.4) + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.7) '@babel/helper-plugin-utils': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-react-display-name@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-react-display-name@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 - '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.27.4) + '@babel/core': 7.27.7 + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.27.7) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-module-imports': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.27.4) - '@babel/types': 7.27.6 + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.27.7) + '@babel/types': 7.27.7 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-react-pure-annotations@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-react-pure-annotations@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-annotate-as-pure': 7.27.3 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-regenerator@7.27.5(@babel/core@7.27.4)': + '@babel/plugin-transform-regenerator@7.27.5(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-regexp-modifiers@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-regexp-modifiers@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.4) + '@babel/core': 7.27.7 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.7) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-runtime@7.27.4(@babel/core@7.27.4)': + '@babel/plugin-transform-runtime@7.27.4(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-module-imports': 7.27.1 '@babel/helper-plugin-utils': 7.27.1 - babel-plugin-polyfill-corejs2: 0.4.13(@babel/core@7.27.4) - babel-plugin-polyfill-corejs3: 0.11.1(@babel/core@7.27.4) - babel-plugin-polyfill-regenerator: 0.6.4(@babel/core@7.27.4) + babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.27.7) + babel-plugin-polyfill-corejs3: 0.11.1(@babel/core@7.27.7) + babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.27.7) semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-spread@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-spread@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 transitivePeerDependencies: - supports-color - '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-typescript@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-typescript@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-annotate-as-pure': 7.27.3 - '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.4) + '@babel/helper-create-class-features-plugin': 7.27.1(@babel/core@7.27.7) '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-skip-transparent-expression-wrappers': 7.27.1 - '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.27.7) transitivePeerDependencies: - supports-color - '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-unicode-property-regex@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-unicode-property-regex@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.4) + '@babel/core': 7.27.7 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.7) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.4) + '@babel/core': 7.27.7 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.7) '@babel/helper-plugin-utils': 7.27.1 - '@babel/plugin-transform-unicode-sets-regex@7.27.1(@babel/core@7.27.4)': + '@babel/plugin-transform-unicode-sets-regex@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 - '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.4) + '@babel/core': 7.27.7 + '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.27.7) '@babel/helper-plugin-utils': 7.27.1 - '@babel/preset-env@7.27.2(@babel/core@7.27.4)': + '@babel/preset-env@7.27.2(@babel/core@7.27.7)': dependencies: - '@babel/compat-data': 7.27.5 - '@babel/core': 7.27.4 + '@babel/compat-data': 7.27.7 + '@babel/core': 7.27.7 '@babel/helper-compilation-targets': 7.27.2 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.27.4) - '@babel/plugin-syntax-import-assertions': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.27.4) - '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-async-generator-functions': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-async-to-generator': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-block-scoping': 7.27.5(@babel/core@7.27.4) - '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-class-static-block': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-classes': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-computed-properties': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-destructuring': 7.27.3(@babel/core@7.27.4) - '@babel/plugin-transform-dotall-regex': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-exponentiation-operator': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-json-strings': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-logical-assignment-operators': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-modules-systemjs': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-named-capturing-groups-regex': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-numeric-separator': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-object-rest-spread': 7.27.3(@babel/core@7.27.4) - '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-optional-catch-binding': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-parameters': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-private-methods': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-private-property-in-object': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-regenerator': 7.27.5(@babel/core@7.27.4) - '@babel/plugin-transform-regexp-modifiers': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-spread': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-unicode-property-regex': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-unicode-sets-regex': 7.27.1(@babel/core@7.27.4) - '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.27.4) - babel-plugin-polyfill-corejs2: 0.4.13(@babel/core@7.27.4) - babel-plugin-polyfill-corejs3: 0.11.1(@babel/core@7.27.4) - babel-plugin-polyfill-regenerator: 0.6.4(@babel/core@7.27.4) + '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.27.7) + '@babel/plugin-syntax-import-assertions': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.27.7) + '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-async-generator-functions': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-async-to-generator': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-block-scoping': 7.27.5(@babel/core@7.27.7) + '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-class-static-block': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-classes': 7.27.7(@babel/core@7.27.7) + '@babel/plugin-transform-computed-properties': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-destructuring': 7.27.7(@babel/core@7.27.7) + '@babel/plugin-transform-dotall-regex': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-exponentiation-operator': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-json-strings': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-logical-assignment-operators': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-modules-systemjs': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-named-capturing-groups-regex': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-numeric-separator': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-object-rest-spread': 7.27.7(@babel/core@7.27.7) + '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-optional-catch-binding': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.27.7) + '@babel/plugin-transform-private-methods': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-private-property-in-object': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-regenerator': 7.27.5(@babel/core@7.27.7) + '@babel/plugin-transform-regexp-modifiers': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-spread': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-unicode-property-regex': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-unicode-sets-regex': 7.27.1(@babel/core@7.27.7) + '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.27.7) + babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.27.7) + babel-plugin-polyfill-corejs3: 0.11.1(@babel/core@7.27.7) + babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.27.7) core-js-compat: 3.43.0 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.27.4)': + '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 - '@babel/types': 7.27.6 + '@babel/types': 7.27.7 esutils: 2.0.3 - '@babel/preset-react@7.27.1(@babel/core@7.27.4)': + '@babel/preset-react@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-transform-react-display-name': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-transform-react-display-name': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.27.7) transitivePeerDependencies: - supports-color - '@babel/preset-typescript@7.27.1(@babel/core@7.27.4)': + '@babel/preset-typescript@7.27.1(@babel/core@7.27.7)': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@babel/helper-plugin-utils': 7.27.1 '@babel/helper-validator-option': 7.27.1 - '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-typescript': 7.27.1(@babel/core@7.27.4) + '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-typescript': 7.27.1(@babel/core@7.27.7) transitivePeerDependencies: - supports-color @@ -7735,22 +7859,22 @@ snapshots: '@babel/template@7.27.2': dependencies: '@babel/code-frame': 7.27.1 - '@babel/parser': 7.27.5 - '@babel/types': 7.27.6 + '@babel/parser': 7.27.7 + '@babel/types': 7.27.7 - '@babel/traverse@7.27.4': + '@babel/traverse@7.27.7': dependencies: '@babel/code-frame': 7.27.1 '@babel/generator': 7.27.5 - '@babel/parser': 7.27.5 + '@babel/parser': 7.27.7 '@babel/template': 7.27.2 - '@babel/types': 7.27.6 + '@babel/types': 7.27.7 debug: 4.4.1 globals: 11.12.0 transitivePeerDependencies: - supports-color - '@babel/types@7.27.6': + '@babel/types@7.27.7': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 @@ -7768,13 +7892,13 @@ snapshots: '@types/tough-cookie': 4.0.5 tough-cookie: 4.1.4 - '@chromatic-com/storybook@4.0.0(storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3))': + '@chromatic-com/storybook@4.0.1(storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2))': dependencies: '@neoconfetti/react': 1.0.0 chromatic: 12.2.0 filesize: 10.1.6 jsonfile: 6.1.0 - storybook: 9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3) + storybook: 9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2) strip-ansi: 7.1.0 transitivePeerDependencies: - '@chromatic-com/cypress' @@ -8084,6 +8208,11 @@ snapshots: wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 + '@jridgewell/gen-mapping@0.3.10': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.2 + '@jridgewell/trace-mapping': 0.3.27 + '@jridgewell/gen-mapping@0.3.8': dependencies: '@jridgewell/set-array': 1.2.1 @@ -8094,18 +8223,25 @@ snapshots: '@jridgewell/set-array@1.2.1': {} - '@jridgewell/source-map@0.3.6': + '@jridgewell/source-map@0.3.8': dependencies: - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/gen-mapping': 0.3.10 + '@jridgewell/trace-mapping': 0.3.27 '@jridgewell/sourcemap-codec@1.5.0': {} + '@jridgewell/sourcemap-codec@1.5.2': {} + '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping@0.3.27': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.2 + '@jsdevtools/ono@7.1.3': {} '@jsep-plugin/assignment@1.3.0(jsep@1.4.0)': @@ -8174,9 +8310,9 @@ snapshots: '@next/swc-win32-x64-msvc@15.3.3': optional: true - '@next/third-parties@15.3.3(next@15.3.3(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': + '@next/third-parties@15.3.3(next@15.3.3(@babel/core@7.27.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)': dependencies: - next: 15.3.3(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 15.3.3(@babel/core@7.27.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react: 18.3.1 third-party-capital: 1.0.20 @@ -8562,7 +8698,7 @@ snapshots: dependencies: playwright: 1.53.1 - '@pmmmwh/react-refresh-webpack-plugin@0.5.16(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-hot-middleware@2.26.1)(webpack@5.99.9(esbuild@0.25.5))': + '@pmmmwh/react-refresh-webpack-plugin@0.5.17(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-hot-middleware@2.26.1)(webpack@5.99.9(esbuild@0.25.5))': dependencies: ansi-html: 0.0.9 core-js-pure: 3.43.0 @@ -9236,7 +9372,7 @@ snapshots: '@sentry/bundler-plugin-core@3.5.0': dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 '@sentry/babel-plugin-component-annotate': 3.5.0 '@sentry/cli': 2.42.2 dotenv: 16.5.0 @@ -9290,7 +9426,7 @@ snapshots: '@sentry/core@9.27.0': {} - '@sentry/nextjs@9.27.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(next@15.3.3(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(webpack@5.99.9(esbuild@0.25.5))': + '@sentry/nextjs@9.27.0(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(next@15.3.3(@babel/core@7.27.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react@18.3.1)(webpack@5.99.9(esbuild@0.25.5))': dependencies: '@opentelemetry/api': 1.9.0 '@opentelemetry/semantic-conventions': 1.34.0 @@ -9303,7 +9439,7 @@ snapshots: '@sentry/vercel-edge': 9.27.0 '@sentry/webpack-plugin': 3.5.0(webpack@5.99.9(esbuild@0.25.5)) chalk: 3.0.0 - next: 15.3.3(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 15.3.3(@babel/core@7.27.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) resolve: 1.22.8 rollup: 4.35.0 stacktrace-parser: 0.1.11 @@ -9573,39 +9709,39 @@ snapshots: '@stoplight/yaml-ast-parser': 0.0.50 tslib: 2.8.1 - '@storybook/addon-a11y@9.0.12(storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3))': + '@storybook/addon-a11y@9.0.14(storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2))': dependencies: '@storybook/global': 5.0.0 axe-core: 4.10.3 - storybook: 9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3) + storybook: 9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2) - '@storybook/addon-docs@9.0.12(@types/react@18.3.17)(storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3))': + '@storybook/addon-docs@9.0.14(@types/react@18.3.17)(storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2))': dependencies: '@mdx-js/react': 3.1.0(@types/react@18.3.17)(react@18.3.1) - '@storybook/csf-plugin': 9.0.12(storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3)) + '@storybook/csf-plugin': 9.0.14(storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2)) '@storybook/icons': 1.4.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - '@storybook/react-dom-shim': 9.0.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3)) + '@storybook/react-dom-shim': 9.0.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2)) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - storybook: 9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3) + storybook: 9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2) ts-dedent: 2.2.0 transitivePeerDependencies: - '@types/react' - '@storybook/addon-links@9.0.12(react@18.3.1)(storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3))': + '@storybook/addon-links@9.0.14(react@18.3.1)(storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2))': dependencies: '@storybook/global': 5.0.0 - storybook: 9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3) + storybook: 9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2) optionalDependencies: react: 18.3.1 - '@storybook/addon-onboarding@9.0.12(storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3))': + '@storybook/addon-onboarding@9.0.14(storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2))': dependencies: - storybook: 9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3) + storybook: 9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2) - '@storybook/builder-webpack5@9.0.12(esbuild@0.25.5)(storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3))(typescript@5.8.3)': + '@storybook/builder-webpack5@9.0.14(esbuild@0.25.5)(storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2))(typescript@5.8.3)': dependencies: - '@storybook/core-webpack': 9.0.12(storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3)) + '@storybook/core-webpack': 9.0.14(storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2)) case-sensitive-paths-webpack-plugin: 2.4.0 cjs-module-lexer: 1.4.3 css-loader: 6.11.0(webpack@5.99.9(esbuild@0.25.5)) @@ -9613,7 +9749,7 @@ snapshots: fork-ts-checker-webpack-plugin: 8.0.0(typescript@5.8.3)(webpack@5.99.9(esbuild@0.25.5)) html-webpack-plugin: 5.6.3(webpack@5.99.9(esbuild@0.25.5)) magic-string: 0.30.17 - storybook: 9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3) + storybook: 9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2) style-loader: 3.3.4(webpack@5.99.9(esbuild@0.25.5)) terser-webpack-plugin: 5.3.14(esbuild@0.25.5)(webpack@5.99.9(esbuild@0.25.5)) ts-dedent: 2.2.0 @@ -9630,14 +9766,14 @@ snapshots: - uglify-js - webpack-cli - '@storybook/core-webpack@9.0.12(storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3))': + '@storybook/core-webpack@9.0.14(storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2))': dependencies: - storybook: 9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3) + storybook: 9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2) ts-dedent: 2.2.0 - '@storybook/csf-plugin@9.0.12(storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3))': + '@storybook/csf-plugin@9.0.14(storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2))': dependencies: - storybook: 9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3) + storybook: 9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2) unplugin: 1.16.1 '@storybook/global@5.0.0': {} @@ -9647,31 +9783,31 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@storybook/nextjs@9.0.12(esbuild@0.25.5)(next@15.3.3(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3))(type-fest@4.41.0)(typescript@5.8.3)(webpack-hot-middleware@2.26.1)(webpack@5.99.9(esbuild@0.25.5))': + '@storybook/nextjs@9.0.14(esbuild@0.25.5)(next@15.3.3(@babel/core@7.27.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2))(type-fest@4.41.0)(typescript@5.8.3)(webpack-hot-middleware@2.26.1)(webpack@5.99.9(esbuild@0.25.5))': dependencies: - '@babel/core': 7.27.4 - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.27.4) - '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.27.4) - '@babel/plugin-syntax-import-assertions': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-numeric-separator': 7.27.1(@babel/core@7.27.4) - '@babel/plugin-transform-object-rest-spread': 7.27.3(@babel/core@7.27.4) - '@babel/plugin-transform-runtime': 7.27.4(@babel/core@7.27.4) - '@babel/preset-env': 7.27.2(@babel/core@7.27.4) - '@babel/preset-react': 7.27.1(@babel/core@7.27.4) - '@babel/preset-typescript': 7.27.1(@babel/core@7.27.4) + '@babel/core': 7.27.7 + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.27.7) + '@babel/plugin-syntax-dynamic-import': 7.8.3(@babel/core@7.27.7) + '@babel/plugin-syntax-import-assertions': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-numeric-separator': 7.27.1(@babel/core@7.27.7) + '@babel/plugin-transform-object-rest-spread': 7.27.7(@babel/core@7.27.7) + '@babel/plugin-transform-runtime': 7.27.4(@babel/core@7.27.7) + '@babel/preset-env': 7.27.2(@babel/core@7.27.7) + '@babel/preset-react': 7.27.1(@babel/core@7.27.7) + '@babel/preset-typescript': 7.27.1(@babel/core@7.27.7) '@babel/runtime': 7.27.6 - '@pmmmwh/react-refresh-webpack-plugin': 0.5.16(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-hot-middleware@2.26.1)(webpack@5.99.9(esbuild@0.25.5)) - '@storybook/builder-webpack5': 9.0.12(esbuild@0.25.5)(storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3))(typescript@5.8.3) - '@storybook/preset-react-webpack': 9.0.12(esbuild@0.25.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3))(typescript@5.8.3) - '@storybook/react': 9.0.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3))(typescript@5.8.3) + '@pmmmwh/react-refresh-webpack-plugin': 0.5.17(react-refresh@0.14.2)(type-fest@4.41.0)(webpack-hot-middleware@2.26.1)(webpack@5.99.9(esbuild@0.25.5)) + '@storybook/builder-webpack5': 9.0.14(esbuild@0.25.5)(storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2))(typescript@5.8.3) + '@storybook/preset-react-webpack': 9.0.14(esbuild@0.25.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2))(typescript@5.8.3) + '@storybook/react': 9.0.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2))(typescript@5.8.3) '@types/semver': 7.7.0 - babel-loader: 9.2.1(@babel/core@7.27.4)(webpack@5.99.9(esbuild@0.25.5)) + babel-loader: 9.2.1(@babel/core@7.27.7)(webpack@5.99.9(esbuild@0.25.5)) css-loader: 6.11.0(webpack@5.99.9(esbuild@0.25.5)) image-size: 2.0.2 loader-utils: 3.3.1 - next: 15.3.3(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 15.3.3(@babel/core@7.27.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) node-polyfill-webpack-plugin: 2.0.1(webpack@5.99.9(esbuild@0.25.5)) postcss: 8.5.6 postcss-loader: 8.1.1(postcss@8.5.6)(typescript@5.8.3)(webpack@5.99.9(esbuild@0.25.5)) @@ -9681,9 +9817,9 @@ snapshots: resolve-url-loader: 5.0.0 sass-loader: 14.2.1(webpack@5.99.9(esbuild@0.25.5)) semver: 7.7.2 - storybook: 9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3) + storybook: 9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2) style-loader: 3.3.4(webpack@5.99.9(esbuild@0.25.5)) - styled-jsx: 5.1.7(@babel/core@7.27.4)(react@18.3.1) + styled-jsx: 5.1.7(@babel/core@7.27.7)(react@18.3.1) tsconfig-paths: 4.2.0 tsconfig-paths-webpack-plugin: 4.2.0 optionalDependencies: @@ -9707,9 +9843,9 @@ snapshots: - webpack-hot-middleware - webpack-plugin-serve - '@storybook/preset-react-webpack@9.0.12(esbuild@0.25.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3))(typescript@5.8.3)': + '@storybook/preset-react-webpack@9.0.14(esbuild@0.25.5)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2))(typescript@5.8.3)': dependencies: - '@storybook/core-webpack': 9.0.12(storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3)) + '@storybook/core-webpack': 9.0.14(storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2)) '@storybook/react-docgen-typescript-plugin': 1.0.6--canary.9.0c3f3b7.0(typescript@5.8.3)(webpack@5.99.9(esbuild@0.25.5)) '@types/semver': 7.7.0 find-up: 7.0.0 @@ -9719,7 +9855,7 @@ snapshots: react-dom: 18.3.1(react@18.3.1) resolve: 1.22.10 semver: 7.7.2 - storybook: 9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3) + storybook: 9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2) tsconfig-paths: 4.2.0 webpack: 5.99.9(esbuild@0.25.5) optionalDependencies: @@ -9745,19 +9881,19 @@ snapshots: transitivePeerDependencies: - supports-color - '@storybook/react-dom-shim@9.0.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3))': + '@storybook/react-dom-shim@9.0.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2))': dependencies: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - storybook: 9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3) + storybook: 9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2) - '@storybook/react@9.0.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3))(typescript@5.8.3)': + '@storybook/react@9.0.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2))(typescript@5.8.3)': dependencies: '@storybook/global': 5.0.0 - '@storybook/react-dom-shim': 9.0.12(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3)) + '@storybook/react-dom-shim': 9.0.14(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2)) react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - storybook: 9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3) + storybook: 9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2) optionalDependencies: typescript: 5.8.3 @@ -9782,7 +9918,7 @@ snapshots: '@supabase/node-fetch': 2.6.15 '@types/phoenix': 1.6.6 '@types/ws': 8.18.1 - ws: 8.18.2 + ws: 8.18.3 transitivePeerDependencies: - bufferutil - utf-8-validate @@ -9814,9 +9950,9 @@ snapshots: dependencies: tslib: 2.8.1 - '@tanstack/eslint-plugin-query@5.78.0(eslint@8.57.1)(typescript@5.8.3)': + '@tanstack/eslint-plugin-query@5.81.2(eslint@8.57.1)(typescript@5.8.3)': dependencies: - '@typescript-eslint/utils': 8.34.1(eslint@8.57.1)(typescript@5.8.3) + '@typescript-eslint/utils': 8.35.0(eslint@8.57.1)(typescript@5.8.3) eslint: 8.57.1 transitivePeerDependencies: - supports-color @@ -9824,11 +9960,11 @@ snapshots: '@tanstack/query-core@5.80.7': {} - '@tanstack/query-devtools@5.80.0': {} + '@tanstack/query-devtools@5.81.2': {} - '@tanstack/react-query-devtools@5.80.10(@tanstack/react-query@5.80.7(react@18.3.1))(react@18.3.1)': + '@tanstack/react-query-devtools@5.81.5(@tanstack/react-query@5.80.7(react@18.3.1))(react@18.3.1)': dependencies: - '@tanstack/query-devtools': 5.80.0 + '@tanstack/query-devtools': 5.81.2 '@tanstack/react-query': 5.80.7(react@18.3.1) react: 18.3.1 @@ -9879,27 +10015,31 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.27.5 - '@babel/types': 7.27.6 + '@babel/parser': 7.27.7 + '@babel/types': 7.27.7 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 '@types/babel__traverse': 7.20.7 '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.27.6 + '@babel/types': 7.27.7 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.27.5 - '@babel/types': 7.27.6 + '@babel/parser': 7.27.7 + '@babel/types': 7.27.7 '@types/babel__traverse@7.20.7': dependencies: - '@babel/types': 7.27.6 + '@babel/types': 7.27.7 '@types/canvas-confetti@1.9.0': {} + '@types/chai@5.2.2': + dependencies: + '@types/deep-eql': 4.0.2 + '@types/connect@3.4.38': dependencies: '@types/node': 22.15.30 @@ -9949,6 +10089,8 @@ snapshots: dependencies: '@types/ms': 2.1.0 + '@types/deep-eql@4.0.2': {} + '@types/doctrine@0.0.9': {} '@types/es-aggregate-error@1.0.6': @@ -9987,7 +10129,7 @@ snapshots: '@types/junit-report-builder@3.0.2': {} - '@types/lodash@4.17.18': {} + '@types/lodash@4.17.19': {} '@types/mdast@4.0.4': dependencies: @@ -10100,15 +10242,51 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/project-service@8.35.0(typescript@5.8.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.35.0(typescript@5.8.3) + '@typescript-eslint/types': 8.35.0 + debug: 4.4.1 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.35.1(typescript@5.8.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.35.1(typescript@5.8.3) + '@typescript-eslint/types': 8.35.1 + debug: 4.4.1 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/scope-manager@8.34.1': dependencies: '@typescript-eslint/types': 8.34.1 '@typescript-eslint/visitor-keys': 8.34.1 + '@typescript-eslint/scope-manager@8.35.0': + dependencies: + '@typescript-eslint/types': 8.35.0 + '@typescript-eslint/visitor-keys': 8.35.0 + + '@typescript-eslint/scope-manager@8.35.1': + dependencies: + '@typescript-eslint/types': 8.35.1 + '@typescript-eslint/visitor-keys': 8.35.1 + '@typescript-eslint/tsconfig-utils@8.34.1(typescript@5.8.3)': dependencies: typescript: 5.8.3 + '@typescript-eslint/tsconfig-utils@8.35.0(typescript@5.8.3)': + dependencies: + typescript: 5.8.3 + + '@typescript-eslint/tsconfig-utils@8.35.1(typescript@5.8.3)': + dependencies: + typescript: 5.8.3 + '@typescript-eslint/type-utils@8.34.1(eslint@8.57.1)(typescript@5.8.3)': dependencies: '@typescript-eslint/typescript-estree': 8.34.1(typescript@5.8.3) @@ -10122,6 +10300,10 @@ snapshots: '@typescript-eslint/types@8.34.1': {} + '@typescript-eslint/types@8.35.0': {} + + '@typescript-eslint/types@8.35.1': {} + '@typescript-eslint/typescript-estree@8.34.1(typescript@5.8.3)': dependencies: '@typescript-eslint/project-service': 8.34.1(typescript@5.8.3) @@ -10138,6 +10320,38 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/typescript-estree@8.35.0(typescript@5.8.3)': + dependencies: + '@typescript-eslint/project-service': 8.35.0(typescript@5.8.3) + '@typescript-eslint/tsconfig-utils': 8.35.0(typescript@5.8.3) + '@typescript-eslint/types': 8.35.0 + '@typescript-eslint/visitor-keys': 8.35.0 + debug: 4.4.1 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.2 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/typescript-estree@8.35.1(typescript@5.8.3)': + dependencies: + '@typescript-eslint/project-service': 8.35.1(typescript@5.8.3) + '@typescript-eslint/tsconfig-utils': 8.35.1(typescript@5.8.3) + '@typescript-eslint/types': 8.35.1 + '@typescript-eslint/visitor-keys': 8.35.1 + debug: 4.4.1 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.2 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/utils@8.34.1(eslint@8.57.1)(typescript@5.8.3)': dependencies: '@eslint-community/eslint-utils': 4.7.0(eslint@8.57.1) @@ -10149,11 +10363,43 @@ snapshots: transitivePeerDependencies: - supports-color + '@typescript-eslint/utils@8.35.0(eslint@8.57.1)(typescript@5.8.3)': + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@8.57.1) + '@typescript-eslint/scope-manager': 8.35.0 + '@typescript-eslint/types': 8.35.0 + '@typescript-eslint/typescript-estree': 8.35.0(typescript@5.8.3) + eslint: 8.57.1 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.35.1(eslint@8.57.1)(typescript@5.8.3)': + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@8.57.1) + '@typescript-eslint/scope-manager': 8.35.1 + '@typescript-eslint/types': 8.35.1 + '@typescript-eslint/typescript-estree': 8.35.1(typescript@5.8.3) + eslint: 8.57.1 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + '@typescript-eslint/visitor-keys@8.34.1': dependencies: '@typescript-eslint/types': 8.34.1 eslint-visitor-keys: 4.2.1 + '@typescript-eslint/visitor-keys@8.35.0': + dependencies: + '@typescript-eslint/types': 8.35.0 + eslint-visitor-keys: 4.2.1 + + '@typescript-eslint/visitor-keys@8.35.1': + dependencies: + '@typescript-eslint/types': 8.35.1 + eslint-visitor-keys: 4.2.1 + '@ungap/structured-clone@1.3.0': {} '@unrs/resolver-binding-android-arm-eabi@1.9.0': @@ -10215,24 +10461,25 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.9.0': optional: true - '@vitest/expect@3.0.9': + '@vitest/expect@3.2.4': dependencies: - '@vitest/spy': 3.0.9 - '@vitest/utils': 3.0.9 + '@types/chai': 5.2.2 + '@vitest/spy': 3.2.4 + '@vitest/utils': 3.2.4 chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/pretty-format@3.0.9': + '@vitest/pretty-format@3.2.4': dependencies: tinyrainbow: 2.0.0 - '@vitest/spy@3.0.9': + '@vitest/spy@3.2.4': dependencies: - tinyspy: 3.0.2 + tinyspy: 4.0.3 - '@vitest/utils@3.0.9': + '@vitest/utils@3.2.4': dependencies: - '@vitest/pretty-format': 3.0.9 + '@vitest/pretty-format': 3.2.4 loupe: 3.1.4 tinyrainbow: 2.0.0 @@ -10557,34 +10804,34 @@ snapshots: axobject-query@4.1.0: {} - babel-loader@9.2.1(@babel/core@7.27.4)(webpack@5.99.9(esbuild@0.25.5)): + babel-loader@9.2.1(@babel/core@7.27.7)(webpack@5.99.9(esbuild@0.25.5)): dependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 find-cache-dir: 4.0.0 schema-utils: 4.3.2 webpack: 5.99.9(esbuild@0.25.5) - babel-plugin-polyfill-corejs2@0.4.13(@babel/core@7.27.4): + babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.27.7): dependencies: - '@babel/compat-data': 7.27.5 - '@babel/core': 7.27.4 - '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.27.4) + '@babel/compat-data': 7.27.7 + '@babel/core': 7.27.7 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.27.7) semver: 6.3.1 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-corejs3@0.11.1(@babel/core@7.27.4): + babel-plugin-polyfill-corejs3@0.11.1(@babel/core@7.27.7): dependencies: - '@babel/core': 7.27.4 - '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.27.4) + '@babel/core': 7.27.7 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.27.7) core-js-compat: 3.43.0 transitivePeerDependencies: - supports-color - babel-plugin-polyfill-regenerator@0.6.4(@babel/core@7.27.4): + babel-plugin-polyfill-regenerator@0.6.5(@babel/core@7.27.7): dependencies: - '@babel/core': 7.27.4 - '@babel/helper-define-polyfill-provider': 0.6.4(@babel/core@7.27.4) + '@babel/core': 7.27.7 + '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.27.7) transitivePeerDependencies: - supports-color @@ -10670,12 +10917,12 @@ snapshots: dependencies: pako: 1.0.11 - browserslist@4.25.0: + browserslist@4.25.1: dependencies: - caniuse-lite: 1.0.30001723 - electron-to-chromium: 1.5.169 + caniuse-lite: 1.0.30001726 + electron-to-chromium: 1.5.177 node-releases: 2.0.19 - update-browserslist-db: 1.1.3(browserslist@4.25.0) + update-browserslist-db: 1.1.3(browserslist@4.25.1) buffer-from@1.1.2: {} @@ -10726,6 +10973,8 @@ snapshots: caniuse-lite@1.0.30001723: {} + caniuse-lite@1.0.30001726: {} + case-sensitive-paths-webpack-plugin@2.4.0: {} ccount@2.0.1: {} @@ -10736,7 +10985,7 @@ snapshots: check-error: 2.1.1 deep-eql: 5.0.2 loupe: 3.1.4 - pathval: 2.0.0 + pathval: 2.0.1 chalk@3.0.0: dependencies: @@ -10857,7 +11106,7 @@ snapshots: concat-map@0.0.1: {} - concurrently@9.1.2: + concurrently@9.2.0: dependencies: chalk: 4.1.2 lodash: 4.17.21 @@ -10881,7 +11130,7 @@ snapshots: core-js-compat@3.43.0: dependencies: - browserslist: 4.25.0 + browserslist: 4.25.1 core-js-pure@3.43.0: {} @@ -10909,6 +11158,13 @@ snapshots: bn.js: 4.12.2 elliptic: 6.6.1 + create-hash@1.1.3: + dependencies: + cipher-base: 1.0.6 + inherits: 2.0.4 + ripemd160: 2.0.1 + sha.js: 2.4.11 + create-hash@1.2.0: dependencies: cipher-base: 1.0.6 @@ -10942,7 +11198,7 @@ snapshots: diffie-hellman: 5.0.3 hash-base: 3.0.5 inherits: 2.0.4 - pbkdf2: 3.1.2 + pbkdf2: 3.1.3 public-encrypt: 4.0.3 randombytes: 2.1.0 randomfill: 1.0.4 @@ -10965,7 +11221,7 @@ snapshots: css-select@4.3.0: dependencies: boolbase: 1.0.0 - css-what: 6.1.0 + css-what: 6.2.2 domhandler: 4.3.1 domutils: 2.8.0 nth-check: 2.1.1 @@ -10976,7 +11232,7 @@ snapshots: css-color-keywords: 1.0.0 postcss-value-parser: 4.2.0 - css-what@6.1.0: {} + css-what@6.2.2: {} css.escape@1.5.1: {} @@ -11198,7 +11454,7 @@ snapshots: eastasianwidth@0.2.0: {} - electron-to-chromium@1.5.169: {} + electron-to-chromium@1.5.177: {} elliptic@6.6.1: dependencies: @@ -11234,7 +11490,7 @@ snapshots: fast-json-parse: 1.0.3 objectorarray: 1.0.5 - enhanced-resolve@5.18.1: + enhanced-resolve@5.18.2: dependencies: graceful-fs: 4.2.11 tapable: 2.2.2 @@ -11541,11 +11797,11 @@ snapshots: string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 - eslint-plugin-storybook@9.0.12(eslint@8.57.1)(storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3))(typescript@5.8.3): + eslint-plugin-storybook@9.0.14(eslint@8.57.1)(storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2))(typescript@5.8.3): dependencies: - '@typescript-eslint/utils': 8.34.1(eslint@8.57.1)(typescript@5.8.3) + '@typescript-eslint/utils': 8.35.1(eslint@8.57.1)(typescript@5.8.3) eslint: 8.57.1 - storybook: 9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3) + storybook: 9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2) transitivePeerDependencies: - supports-color - typescript @@ -11827,9 +12083,9 @@ snapshots: functions-have-names@1.2.3: {} - geist@1.4.2(next@15.3.3(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)): + geist@1.4.2(next@15.3.3(@babel/core@7.27.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)): dependencies: - next: 15.3.3(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + next: 15.3.3(@babel/core@7.27.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) gensync@1.0.0-beta.2: {} @@ -11948,6 +12204,10 @@ snapshots: dependencies: has-symbols: 1.1.0 + hash-base@2.0.2: + dependencies: + inherits: 2.0.4 + hash-base@3.0.5: dependencies: inherits: 2.0.4 @@ -12010,7 +12270,7 @@ snapshots: he: 1.2.0 param-case: 3.0.4 relateurl: 0.2.7 - terser: 5.42.0 + terser: 5.43.1 html-url-attributes@3.0.1: {} @@ -12460,11 +12720,11 @@ snapshots: magic-string@0.30.17: dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.2 magic-string@0.30.8: dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.2 make-dir@3.1.0: dependencies: @@ -12830,7 +13090,7 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - next@15.3.3(@babel/core@7.27.4)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + next@15.3.3(@babel/core@7.27.7)(@opentelemetry/api@1.9.0)(@playwright/test@1.53.1)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@next/env': 15.3.3 '@swc/counter': 0.1.3 @@ -12840,7 +13100,7 @@ snapshots: postcss: 8.4.31 react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - styled-jsx: 5.1.6(@babel/core@7.27.4)(react@18.3.1) + styled-jsx: 5.1.6(@babel/core@7.27.7)(react@18.3.1) optionalDependencies: '@next/swc-darwin-arm64': 15.3.3 '@next/swc-darwin-x64': 15.3.3 @@ -13130,7 +13390,7 @@ snapshots: browserify-aes: 1.2.0 evp_bytestokey: 1.0.3 hash-base: 3.0.5 - pbkdf2: 3.1.2 + pbkdf2: 3.1.3 safe-buffer: 5.2.1 parse-entities@4.0.2: @@ -13178,15 +13438,16 @@ snapshots: path-type@4.0.0: {} - pathval@2.0.0: {} + pathval@2.0.1: {} - pbkdf2@3.1.2: + pbkdf2@3.1.3: dependencies: - create-hash: 1.2.0 + create-hash: 1.1.3 create-hmac: 1.1.7 - ripemd160: 2.0.2 + ripemd160: 2.0.1 safe-buffer: 5.2.1 sha.js: 2.4.11 + to-buffer: 1.2.1 pg-int8@1.0.1: {} @@ -13328,11 +13589,11 @@ snapshots: prelude-ls@1.2.1: {} - prettier-plugin-tailwindcss@0.6.12(prettier@3.5.3): + prettier-plugin-tailwindcss@0.6.13(prettier@3.6.2): dependencies: - prettier: 3.5.3 + prettier: 3.6.2 - prettier@3.5.3: {} + prettier@3.6.2: {} pretty-error@4.0.0: dependencies: @@ -13414,9 +13675,9 @@ snapshots: react-docgen@7.1.1: dependencies: - '@babel/core': 7.27.4 - '@babel/traverse': 7.27.4 - '@babel/types': 7.27.6 + '@babel/core': 7.27.7 + '@babel/traverse': 7.27.7 + '@babel/types': 7.27.7 '@types/babel__core': 7.20.5 '@types/babel__traverse': 7.20.7 '@types/doctrine': 0.0.9 @@ -13725,6 +13986,11 @@ snapshots: dependencies: glob: 7.2.3 + ripemd160@2.0.1: + dependencies: + hash-base: 2.0.2 + inherits: 2.0.4 + ripemd160@2.0.2: dependencies: hash-base: 3.0.5 @@ -13992,21 +14258,21 @@ snapshots: es-errors: 1.3.0 internal-slot: 1.1.0 - storybook@9.0.12(@testing-library/dom@10.4.0)(prettier@3.5.3): + storybook@9.0.14(@testing-library/dom@10.4.0)(prettier@3.6.2): dependencies: '@storybook/global': 5.0.0 '@testing-library/jest-dom': 6.6.3 '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.0) - '@vitest/expect': 3.0.9 - '@vitest/spy': 3.0.9 + '@vitest/expect': 3.2.4 + '@vitest/spy': 3.2.4 better-opn: 3.0.2 esbuild: 0.25.5 esbuild-register: 3.6.0(esbuild@0.25.5) recast: 0.23.11 semver: 7.7.2 - ws: 8.18.2 + ws: 8.18.3 optionalDependencies: - prettier: 3.5.3 + prettier: 3.6.2 transitivePeerDependencies: - '@testing-library/dom' - bufferutil @@ -14154,19 +14420,19 @@ snapshots: stylis: 4.3.2 tslib: 2.6.2 - styled-jsx@5.1.6(@babel/core@7.27.4)(react@18.3.1): + styled-jsx@5.1.6(@babel/core@7.27.7)(react@18.3.1): dependencies: client-only: 0.0.1 react: 18.3.1 optionalDependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 - styled-jsx@5.1.7(@babel/core@7.27.4)(react@18.3.1): + styled-jsx@5.1.7(@babel/core@7.27.7)(react@18.3.1): dependencies: client-only: 0.0.1 react: 18.3.1 optionalDependencies: - '@babel/core': 7.27.4 + '@babel/core': 7.27.7 stylis@4.3.2: {} @@ -14243,18 +14509,18 @@ snapshots: terser-webpack-plugin@5.3.14(esbuild@0.25.5)(webpack@5.99.9(esbuild@0.25.5)): dependencies: - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/trace-mapping': 0.3.27 jest-worker: 27.5.1 schema-utils: 4.3.2 serialize-javascript: 6.0.2 - terser: 5.42.0 + terser: 5.43.1 webpack: 5.99.9(esbuild@0.25.5) optionalDependencies: esbuild: 0.25.5 - terser@5.42.0: + terser@5.43.1: dependencies: - '@jridgewell/source-map': 0.3.6 + '@jridgewell/source-map': 0.3.8 acorn: 8.15.0 commander: 2.20.3 source-map-support: 0.5.21 @@ -14284,7 +14550,13 @@ snapshots: tinyrainbow@2.0.0: {} - tinyspy@3.0.2: {} + tinyspy@4.0.3: {} + + to-buffer@1.2.1: + dependencies: + isarray: 2.0.5 + safe-buffer: 5.2.1 + typed-array-buffer: 1.0.3 to-regex-range@5.0.1: dependencies: @@ -14320,7 +14592,7 @@ snapshots: tsconfig-paths-webpack-plugin@4.2.0: dependencies: chalk: 4.1.2 - enhanced-resolve: 5.18.1 + enhanced-resolve: 5.18.2 tapable: 2.2.2 tsconfig-paths: 4.2.0 @@ -14504,9 +14776,9 @@ snapshots: '@unrs/resolver-binding-win32-ia32-msvc': 1.9.0 '@unrs/resolver-binding-win32-x64-msvc': 1.9.0 - update-browserslist-db@1.1.3(browserslist@4.25.0): + update-browserslist-db@1.1.3(browserslist@4.25.1): dependencies: - browserslist: 4.25.0 + browserslist: 4.25.1 escalade: 3.2.0 picocolors: 1.1.1 @@ -14567,6 +14839,15 @@ snapshots: validator@13.15.15: {} + vaul@1.1.2(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@radix-ui/react-dialog': 1.1.14(@types/react-dom@18.3.5(@types/react@18.3.17))(@types/react@18.3.17)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + vfile-message@4.0.2: dependencies: '@types/unist': 3.0.3 @@ -14625,6 +14906,8 @@ snapshots: webpack-sources@3.3.2: {} + webpack-sources@3.3.3: {} + webpack-virtual-modules@0.5.0: {} webpack-virtual-modules@0.6.2: {} @@ -14638,9 +14921,9 @@ snapshots: '@webassemblyjs/wasm-edit': 1.14.1 '@webassemblyjs/wasm-parser': 1.14.1 acorn: 8.15.0 - browserslist: 4.25.0 + browserslist: 4.25.1 chrome-trace-event: 1.0.4 - enhanced-resolve: 5.18.1 + enhanced-resolve: 5.18.2 es-module-lexer: 1.7.0 eslint-scope: 5.1.1 events: 3.3.0 @@ -14654,7 +14937,7 @@ snapshots: tapable: 2.2.2 terser-webpack-plugin: 5.3.14(esbuild@0.25.5)(webpack@5.99.9(esbuild@0.25.5)) watchpack: 2.4.4 - webpack-sources: 3.3.2 + webpack-sources: 3.3.3 transitivePeerDependencies: - '@swc/core' - esbuild @@ -14732,7 +15015,7 @@ snapshots: wrappy@1.0.2: {} - ws@8.18.2: {} + ws@8.18.3: {} xmlbuilder@15.1.1: {} diff --git a/autogpt_platform/frontend/src/app/(platform)/build/page.tsx b/autogpt_platform/frontend/src/app/(platform)/build/page.tsx index e4a001819f..ed8ff0a1ba 100644 --- a/autogpt_platform/frontend/src/app/(platform)/build/page.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/build/page.tsx @@ -14,11 +14,13 @@ export default function BuilderPage() { completeStep("BUILDER_OPEN"); }, [completeStep]); + const _graphVersion = query.get("flowVersion"); + const graphVersion = _graphVersion ? parseInt(_graphVersion) : undefined return ( ); } diff --git a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/page.tsx b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/page.tsx index b993e85c0a..84aae2898f 100644 --- a/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/page.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/library/agents/[id]/page.tsx @@ -20,6 +20,8 @@ import { LibraryAgentID, Schedule, ScheduleID, + LibraryAgentPreset, + LibraryAgentPresetID, } from "@/lib/autogpt-server-api"; import type { ButtonAction } from "@/components/agptui/types"; @@ -52,22 +54,27 @@ export default function AgentRunsPage(): React.ReactElement { const [graph, setGraph] = useState(null); // Graph version corresponding to LibraryAgent const [agent, setAgent] = useState(null); const [agentRuns, setAgentRuns] = useState([]); + const [agentPresets, setAgentPresets] = useState([]); const [schedules, setSchedules] = useState([]); const [selectedView, selectView] = useState< | { type: "run"; id?: GraphExecutionID } + | { type: "preset"; id: LibraryAgentPresetID } | { type: "schedule"; id: ScheduleID } >({ type: "run" }); const [selectedRun, setSelectedRun] = useState< GraphExecution | GraphExecutionMeta | null >(null); - const [selectedSchedule, setSelectedSchedule] = useState( - null, - ); + const selectedSchedule = + selectedView.type == "schedule" + ? schedules.find((s) => s.id == selectedView.id) + : null; const [isFirstLoad, setIsFirstLoad] = useState(true); const [agentDeleteDialogOpen, setAgentDeleteDialogOpen] = useState(false); const [confirmingDeleteAgentRun, setConfirmingDeleteAgentRun] = useState(null); + const [confirmingDeleteAgentPreset, setConfirmingDeleteAgentPreset] = + useState(null); const { state: onboardingState, updateState: updateOnboardingState, @@ -90,9 +97,12 @@ export default function AgentRunsPage(): React.ReactElement { selectView({ type: "run", id }); }, []); - const selectSchedule = useCallback((schedule: Schedule) => { - selectView({ type: "schedule", id: schedule.id }); - setSelectedSchedule(schedule); + const selectPreset = useCallback((id: LibraryAgentPresetID) => { + selectView({ type: "preset", id }); + }, []); + + const selectSchedule = useCallback((id: ScheduleID) => { + selectView({ type: "schedule", id }); }, []); const graphVersions = useRef>({}); @@ -143,12 +153,19 @@ export default function AgentRunsPage(): React.ReactElement { (_graph) => (graph && graph.version == _graph.version) || setGraph(_graph), ); - api.getGraphExecutions(agent.graph_id).then((agentRuns) => { - setAgentRuns(agentRuns); + Promise.all([ + api.getGraphExecutions(agent.graph_id), + api.listLibraryAgentPresets({ + graph_id: agent.graph_id, + page_size: 100, + }), + ]).then(([runs, presets]) => { + setAgentRuns(runs); + setAgentPresets(presets.presets); - // Preload the corresponding graph versions - new Set(agentRuns.map((run) => run.graph_version)).forEach((version) => - getGraphVersion(agent.graph_id, version), + // Preload the corresponding graph versions for the latest 10 runs + new Set(runs.slice(0, 10).map((run) => run.graph_version)).forEach( + (version) => getGraphVersion(agent.graph_id, version), ); }); }); @@ -157,16 +174,33 @@ export default function AgentRunsPage(): React.ReactElement { // On first load: select the latest run useEffect(() => { // Only for first load or first execution - if (selectedView.id || !isFirstLoad || agentRuns.length == 0) return; - setIsFirstLoad(false); + if (selectedView.id || !isFirstLoad) return; + if (agentRuns.length == 0 && agentPresets.length == 0) return; - const latestRun = agentRuns.reduce((latest, current) => { - if (latest.started_at && !current.started_at) return current; - else if (!latest.started_at) return latest; - return latest.started_at > current.started_at ? latest : current; - }, agentRuns[0]); - selectView({ type: "run", id: latestRun.id }); - }, [agentRuns, isFirstLoad, selectedView.id, selectView]); + setIsFirstLoad(false); + if (agentRuns.length > 0) { + // select latest run + const latestRun = agentRuns.reduce((latest, current) => { + if (latest.started_at && !current.started_at) return current; + else if (!latest.started_at) return latest; + return latest.started_at > current.started_at ? latest : current; + }, agentRuns[0]); + selectRun(latestRun.id); + } else { + // select top preset + const latestPreset = agentPresets.toSorted( + (a, b) => b.updated_at.getTime() - a.updated_at.getTime(), + )[0]; + selectPreset(latestPreset.id); + } + }, [ + isFirstLoad, + selectedView.id, + agentRuns, + agentPresets, + selectRun, + selectPreset, + ]); // Initial load useEffect(() => { @@ -281,11 +315,8 @@ export default function AgentRunsPage(): React.ReactElement { const fetchSchedules = useCallback(async () => { if (!agent) return; - // TODO: filter in backend - https://github.com/Significant-Gravitas/AutoGPT/issues/9183 - setSchedules( - (await api.listSchedules()).filter((s) => s.graph_id == agent.graph_id), - ); - }, [api, agent]); + setSchedules(await api.listGraphExecutionSchedules(agent.graph_id)); + }, [api, agent?.graph_id]); useEffect(() => { fetchSchedules(); @@ -304,15 +335,48 @@ export default function AgentRunsPage(): React.ReactElement { if (selectedView.type == "run" && selectedView.id == run.id) { openRunDraftView(); } - setAgentRuns(agentRuns.filter((r) => r.id !== run.id)); + setAgentRuns((runs) => runs.filter((r) => r.id !== run.id)); }, - [agentRuns, api, selectedView, openRunDraftView], + [api, selectedView, openRunDraftView], + ); + + const deletePreset = useCallback( + async (presetID: LibraryAgentPresetID) => { + await api.deleteLibraryAgentPreset(presetID); + + setConfirmingDeleteAgentPreset(null); + if (selectedView.type == "preset" && selectedView.id == presetID) { + openRunDraftView(); + } + setAgentPresets((presets) => presets.filter((p) => p.id !== presetID)); + }, + [api, selectedView, openRunDraftView], ); const deleteSchedule = useCallback( async (scheduleID: ScheduleID) => { - const removedSchedule = await api.deleteSchedule(scheduleID); - setSchedules(schedules.filter((s) => s.id !== removedSchedule.id)); + const removedSchedule = + await api.deleteGraphExecutionSchedule(scheduleID); + + setSchedules((schedules) => { + const newSchedules = schedules.filter( + (s) => s.id !== removedSchedule.id, + ); + if ( + selectedView.type == "schedule" && + selectedView.id == removedSchedule.id + ) { + if (newSchedules.length > 0) { + // Select next schedule if available + selectSchedule(newSchedules[0].id); + } else { + // Reset to draft view if current schedule was deleted + openRunDraftView(); + } + } + return newSchedules; + }); + openRunDraftView(); }, [schedules, api], ); @@ -370,11 +434,30 @@ export default function AgentRunsPage(): React.ReactElement { [agent, downloadGraph], ); - const onRun = useCallback( - (runID: GraphExecutionID) => { - selectRun(runID); + const onCreateSchedule = useCallback( + (schedule: Schedule) => { + setSchedules((prev) => [...prev, schedule]); + selectSchedule(schedule.id); }, - [selectRun], + [selectView], + ); + + const onCreatePreset = useCallback( + (preset: LibraryAgentPreset) => { + setAgentPresets((prev) => [...prev, preset]); + selectPreset(preset.id); + }, + [selectPreset], + ); + + const onUpdatePreset = useCallback( + (updated: LibraryAgentPreset) => { + setAgentPresets((prev) => + prev.map((p) => (p.id === updated.id ? updated : p)), + ); + selectPreset(updated.id); + }, + [selectPreset], ); if (!agent || !graph) { @@ -389,14 +472,16 @@ export default function AgentRunsPage(): React.ReactElement { className="agpt-div w-full border-b lg:w-auto lg:border-b-0 lg:border-r" agent={agent} agentRuns={agentRuns} + agentPresets={agentPresets} schedules={schedules} selectedView={selectedView} - allowDraftNewRun={!graph.has_webhook_trigger} onSelectRun={selectRun} + onSelectPreset={selectPreset} onSelectSchedule={selectSchedule} onSelectDraftNewRun={openRunDraftView} - onDeleteRun={setConfirmingDeleteAgentRun} - onDeleteSchedule={(id) => deleteSchedule(id)} + doDeleteRun={setConfirmingDeleteAgentRun} + doDeletePreset={setConfirmingDeleteAgentPreset} + doDeleteSchedule={deleteSchedule} />
@@ -417,14 +502,30 @@ export default function AgentRunsPage(): React.ReactElement { graph={graphVersions.current[selectedRun.graph_version] ?? graph} run={selectedRun} agentActions={agentActions} - onRun={onRun} + onRun={selectRun} deleteRun={() => setConfirmingDeleteAgentRun(selectedRun)} /> ) ) : selectedView.type == "run" ? ( + /* Draft new runs / Create new presets */ + ) : selectedView.type == "preset" ? ( + /* Edit & update presets */ + preset.id == selectedView.id)! + } + onRun={selectRun} + onCreateSchedule={onCreateSchedule} + onUpdatePreset={onUpdatePreset} + doDeletePreset={setConfirmingDeleteAgentPreset} agentActions={agentActions} /> ) : selectedView.type == "schedule" ? ( @@ -432,8 +533,10 @@ export default function AgentRunsPage(): React.ReactElement { ) ) : null) || } @@ -444,9 +547,7 @@ export default function AgentRunsPage(): React.ReactElement { onOpenChange={setAgentDeleteDialogOpen} onDoDelete={() => agent && - api - .updateLibraryAgent(agent.id, { is_deleted: true }) - .then(() => router.push("/library")) + api.deleteLibraryAgent(agent.id).then(() => router.push("/library")) } /> @@ -458,6 +559,15 @@ export default function AgentRunsPage(): React.ReactElement { confirmingDeleteAgentRun && deleteRun(confirmingDeleteAgentRun) } /> + !open && setConfirmingDeleteAgentPreset(null)} + onDoDelete={() => + confirmingDeleteAgentPreset && + deletePreset(confirmingDeleteAgentPreset) + } + /> {/* Copy agent confirmation dialog */} ; diff --git a/autogpt_platform/frontend/src/components/library/library-action-sub-header.tsx b/autogpt_platform/frontend/src/app/(platform)/library/components/LibraryActionSubHeader/LibraryActionSubHeader.tsx similarity index 61% rename from autogpt_platform/frontend/src/components/library/library-action-sub-header.tsx rename to autogpt_platform/frontend/src/app/(platform)/library/components/LibraryActionSubHeader/LibraryActionSubHeader.tsx index 05fe6d48af..bb76233b6a 100644 --- a/autogpt_platform/frontend/src/components/library/library-action-sub-header.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/library/components/LibraryActionSubHeader/LibraryActionSubHeader.tsx @@ -1,11 +1,14 @@ "use client"; -import { useLibraryPageContext } from "@/app/(platform)/library/state-provider"; -import LibrarySortMenu from "./library-sort-menu"; +import LibrarySortMenu from "../LibrarySortMenu/LibrarySortMenu"; -export default function LibraryActionSubHeader(): React.ReactNode { - const { agents } = useLibraryPageContext(); +interface LibraryActionSubHeaderProps { + agentCount: number; +} +export default function LibraryActionSubHeader({ + agentCount, +}: LibraryActionSubHeaderProps) { return (
@@ -13,7 +16,7 @@ export default function LibraryActionSubHeader(): React.ReactNode { My agents - {agents.length} agents + {agentCount} agents
diff --git a/autogpt_platform/frontend/src/components/library/library-agent-card.tsx b/autogpt_platform/frontend/src/app/(platform)/library/components/LibraryAgentCard/LibraryAgentCard.tsx similarity index 95% rename from autogpt_platform/frontend/src/components/library/library-agent-card.tsx rename to autogpt_platform/frontend/src/app/(platform)/library/components/LibraryAgentCard/LibraryAgentCard.tsx index a2ee2ccc56..c9d21d5629 100644 --- a/autogpt_platform/frontend/src/components/library/library-agent-card.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/library/components/LibraryAgentCard/LibraryAgentCard.tsx @@ -1,7 +1,12 @@ import Link from "next/link"; import Image from "next/image"; -import { LibraryAgent } from "@/lib/autogpt-server-api"; + import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; +import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent"; + +interface LibraryAgentCardProps { + agent: LibraryAgent; +} export default function LibraryAgentCard({ agent: { @@ -13,9 +18,7 @@ export default function LibraryAgentCard({ creator_image_url, image_url, }, -}: { - agent: LibraryAgent; -}): React.ReactNode { +}: LibraryAgentCardProps) { return (
( +
+ ); + + return ( + <> + {/* TODO: We need a new endpoint on backend that returns total number of agents */} + +
+ {agentLoading ? ( +
+ +
+ ) : ( + <> +
+ {agents.map((agent) => ( + + ))} +
+ {(isFetchingNextPage || isSearching) && ( +
+ +
+ )} + + )} +
+ + ); +} diff --git a/autogpt_platform/frontend/src/app/(platform)/library/components/LibraryAgentList/useLibraryAgentList.ts b/autogpt_platform/frontend/src/app/(platform)/library/components/LibraryAgentList/useLibraryAgentList.ts new file mode 100644 index 0000000000..d5d9e41933 --- /dev/null +++ b/autogpt_platform/frontend/src/app/(platform)/library/components/LibraryAgentList/useLibraryAgentList.ts @@ -0,0 +1,66 @@ +import { useGetV2ListLibraryAgentsInfinite } from "@/app/api/__generated__/endpoints/library/library"; +import { LibraryAgentResponse } from "@/app/api/__generated__/models/libraryAgentResponse"; +import { useScrollThreshold } from "@/hooks/useScrollThreshold"; +import { useCallback } from "react"; +import { useLibraryPageContext } from "../state-provider"; + +export const useLibraryAgentList = () => { + const { searchTerm, librarySort } = useLibraryPageContext(); + const { + data: agents, + fetchNextPage, + hasNextPage, + isFetchingNextPage, + isLoading: agentLoading, + isFetching, + } = useGetV2ListLibraryAgentsInfinite( + { + page: 1, + page_size: 8, + search_term: searchTerm || undefined, + sort_by: librarySort, + }, + { + query: { + getNextPageParam: (lastPage) => { + const pagination = (lastPage.data as LibraryAgentResponse).pagination; + const isMore = + pagination.current_page * pagination.page_size < + pagination.total_items; + + return isMore ? pagination.current_page + 1 : undefined; + }, + }, + }, + ); + + const handleInfiniteScroll = useCallback( + (scrollY: number) => { + if (!hasNextPage || isFetchingNextPage) return; + + const { scrollHeight, clientHeight } = document.documentElement; + const SCROLL_THRESHOLD = 20; + + if (scrollY + clientHeight >= scrollHeight - SCROLL_THRESHOLD) { + fetchNextPage(); + } + }, + [hasNextPage, isFetchingNextPage, fetchNextPage], + ); + + useScrollThreshold(handleInfiniteScroll, 50); + + const allAgents = + agents?.pages.flatMap((page) => { + const data = page.data as LibraryAgentResponse; + return data.agents; + }) ?? []; + + return { + allAgents, + agentLoading, + isFetchingNextPage, + hasNextPage, + isSearching: isFetching && !isFetchingNextPage, + }; +}; diff --git a/autogpt_platform/frontend/src/components/library/library-notification-card.tsx b/autogpt_platform/frontend/src/app/(platform)/library/components/LibraryNotificationCard/LibraryNotificationCard.tsx similarity index 100% rename from autogpt_platform/frontend/src/components/library/library-notification-card.tsx rename to autogpt_platform/frontend/src/app/(platform)/library/components/LibraryNotificationCard/LibraryNotificationCard.tsx diff --git a/autogpt_platform/frontend/src/components/library/library-notification-dropdown.tsx b/autogpt_platform/frontend/src/app/(platform)/library/components/LibraryNotificationDropdown/LibraryNotificationDropdown.tsx similarity index 97% rename from autogpt_platform/frontend/src/components/library/library-notification-dropdown.tsx rename to autogpt_platform/frontend/src/app/(platform)/library/components/LibraryNotificationDropdown/LibraryNotificationDropdown.tsx index 8bb131ceb4..f38ade2359 100644 --- a/autogpt_platform/frontend/src/components/library/library-notification-dropdown.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/library/components/LibraryNotificationDropdown/LibraryNotificationDropdown.tsx @@ -11,9 +11,9 @@ import { DropdownMenuLabel, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; -import LibraryNotificationCard, { +import NotificationCard, { NotificationCardData, -} from "./library-notification-card"; +} from "../LibraryNotificationCard/LibraryNotificationCard"; export default function LibraryNotificationDropdown(): React.ReactNode { const controls = useAnimationControls(); @@ -109,7 +109,7 @@ export default function LibraryNotificationDropdown(): React.ReactNode { {notifications && notifications.length ? ( notifications.map((notification) => ( - setNotifications((prev) => { diff --git a/autogpt_platform/frontend/src/app/(platform)/library/components/LibrarySearchBar/LibrarySearchBar.tsx b/autogpt_platform/frontend/src/app/(platform)/library/components/LibrarySearchBar/LibrarySearchBar.tsx new file mode 100644 index 0000000000..099f036052 --- /dev/null +++ b/autogpt_platform/frontend/src/app/(platform)/library/components/LibrarySearchBar/LibrarySearchBar.tsx @@ -0,0 +1,38 @@ +"use client"; +import { Input } from "@/components/ui/input"; +import { Search, X } from "lucide-react"; +import { useLibrarySearchbar } from "./useLibrarySearchbar"; + +export default function LibrarySearchBar(): React.ReactNode { + const { handleSearchInput, handleClear, setIsFocused, isFocused, inputRef } = + useLibrarySearchbar(); + return ( +
inputRef.current?.focus()} + className="relative z-[21] mx-auto flex h-[50px] w-full max-w-[500px] flex-1 cursor-pointer items-center rounded-[45px] bg-[#EDEDED] px-[24px] py-[10px]" + > + + + setIsFocused(true)} + onBlur={() => !inputRef.current?.value && setIsFocused(false)} + onChange={handleSearchInput} + className="flex-1 border-none font-sans text-[16px] font-normal leading-7 shadow-none focus:shadow-none focus:ring-0" + type="text" + placeholder="Search agents" + /> + + {isFocused && inputRef.current?.value && ( + + )} +
+ ); +} diff --git a/autogpt_platform/frontend/src/app/(platform)/library/components/LibrarySearchBar/useLibrarySearchbar.tsx b/autogpt_platform/frontend/src/app/(platform)/library/components/LibrarySearchBar/useLibrarySearchbar.tsx new file mode 100644 index 0000000000..f6428c6c4e --- /dev/null +++ b/autogpt_platform/frontend/src/app/(platform)/library/components/LibrarySearchBar/useLibrarySearchbar.tsx @@ -0,0 +1,36 @@ +import { useRef, useState } from "react"; +import { useLibraryPageContext } from "../state-provider"; +import { debounce } from "lodash"; + +export const useLibrarySearchbar = () => { + const inputRef = useRef(null); + const [isFocused, setIsFocused] = useState(false); + const { setSearchTerm } = useLibraryPageContext(); + + const debouncedSearch = debounce((value: string) => { + setSearchTerm(value); + }, 300); + + const handleSearchInput = (e: React.ChangeEvent) => { + const searchTerm = e.target.value; + debouncedSearch(searchTerm); + }; + + const handleClear = (e: React.MouseEvent) => { + if (inputRef.current) { + inputRef.current.value = ""; + inputRef.current.blur(); + setSearchTerm(""); + e.preventDefault(); + } + setIsFocused(false); + }; + + return { + handleClear, + handleSearchInput, + isFocused, + inputRef, + setIsFocused, + }; +}; diff --git a/autogpt_platform/frontend/src/components/library/library-sort-menu.tsx b/autogpt_platform/frontend/src/app/(platform)/library/components/LibrarySortMenu/LibrarySortMenu.tsx similarity index 52% rename from autogpt_platform/frontend/src/components/library/library-sort-menu.tsx rename to autogpt_platform/frontend/src/app/(platform)/library/components/LibrarySortMenu/LibrarySortMenu.tsx index c5ac2e819c..d1d973d295 100644 --- a/autogpt_platform/frontend/src/components/library/library-sort-menu.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/library/components/LibrarySortMenu/LibrarySortMenu.tsx @@ -1,6 +1,4 @@ -import { useBackendAPI } from "@/lib/autogpt-server-api/context"; -import { LibraryAgentSortEnum } from "@/lib/autogpt-server-api/types"; -import { useLibraryPageContext } from "@/app/(platform)/library/state-provider"; +"use client"; import { ArrowDownNarrowWideIcon } from "lucide-react"; import { Select, @@ -10,24 +8,11 @@ import { SelectTrigger, SelectValue, } from "@/components/ui/select"; +import { LibraryAgentSort } from "@/app/api/__generated__/models/libraryAgentSort"; +import { useLibrarySortMenu } from "./useLibrarySortMenu"; export default function LibrarySortMenu(): React.ReactNode { - const api = useBackendAPI(); - const { setAgentLoading, setAgents, setLibrarySort, searchTerm } = - useLibraryPageContext(); - const handleSortChange = async (value: LibraryAgentSortEnum) => { - setLibrarySort(value); - setAgentLoading(true); - await new Promise((resolve) => setTimeout(resolve, 1000)); - const response = await api.listLibraryAgents({ - search_term: searchTerm, - sort_by: value, - page: 1, - }); - setAgents(response.agents); - setAgentLoading(false); - }; - + const { handleSortChange } = useLibrarySortMenu(); return (
sort by @@ -38,10 +23,10 @@ export default function LibrarySortMenu(): React.ReactNode { - + Creation Date - + Last Modified diff --git a/autogpt_platform/frontend/src/app/(platform)/library/components/LibrarySortMenu/useLibrarySortMenu.ts b/autogpt_platform/frontend/src/app/(platform)/library/components/LibrarySortMenu/useLibrarySortMenu.ts new file mode 100644 index 0000000000..d2575c8936 --- /dev/null +++ b/autogpt_platform/frontend/src/app/(platform)/library/components/LibrarySortMenu/useLibrarySortMenu.ts @@ -0,0 +1,27 @@ +import { LibraryAgentSort } from "@/app/api/__generated__/models/libraryAgentSort"; +import { useLibraryPageContext } from "../state-provider"; + +export const useLibrarySortMenu = () => { + const { setLibrarySort } = useLibraryPageContext(); + + const handleSortChange = (value: LibraryAgentSort) => { + // Simply updating the sort state - React Query will handle the rest + setLibrarySort(value); + }; + + const getSortLabel = (sort: LibraryAgentSort) => { + switch (sort) { + case LibraryAgentSort.createdAt: + return "Creation Date"; + case LibraryAgentSort.updatedAt: + return "Last Modified"; + default: + return "Last Modified"; + } + }; + + return { + handleSortChange, + getSortLabel, + }; +}; diff --git a/autogpt_platform/frontend/src/components/library/library-upload-agent-dialog.tsx b/autogpt_platform/frontend/src/app/(platform)/library/components/LibraryUploadAgentDialog/LibraryUploadAgentDialog.tsx similarity index 68% rename from autogpt_platform/frontend/src/components/library/library-upload-agent-dialog.tsx rename to autogpt_platform/frontend/src/app/(platform)/library/components/LibraryUploadAgentDialog/LibraryUploadAgentDialog.tsx index a032f23361..e095da4f5f 100644 --- a/autogpt_platform/frontend/src/components/library/library-upload-agent-dialog.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/library/components/LibraryUploadAgentDialog/LibraryUploadAgentDialog.tsx @@ -1,5 +1,4 @@ "use client"; -import { useState } from "react"; import { Upload, X } from "lucide-react"; import { Button } from "@/components/agptui/Button"; import { @@ -10,8 +9,6 @@ import { DialogTrigger, } from "@/components/ui/dialog"; import { z } from "zod"; -import { useForm } from "react-hook-form"; -import { zodResolver } from "@hookform/resolvers/zod"; import { FileUploader } from "react-drag-drop-files"; import { Form, @@ -23,13 +20,7 @@ import { } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; -import { - Graph, - GraphCreatable, - sanitizeImportedGraph, -} from "@/lib/autogpt-server-api"; -import { useBackendAPI } from "@/lib/autogpt-server-api/context"; -import { useToast } from "@/components/ui/use-toast"; +import { useLibraryUploadAgentDialog } from "./useLibraryUploadAgentDialog"; const fileTypes = ["JSON"]; @@ -37,98 +28,24 @@ const fileSchema = z.custom((val) => val instanceof File, { message: "Must be a File object", }); -const formSchema = z.object({ +export const uploadAgentFormSchema = z.object({ agentFile: fileSchema, agentName: z.string().min(1, "Agent name is required"), agentDescription: z.string(), }); export default function LibraryUploadAgentDialog(): React.ReactNode { - const [isDroped, setisDroped] = useState(false); - const [isLoading, setIsLoading] = useState(false); - const [isOpen, setIsOpen] = useState(false); - const api = useBackendAPI(); - const { toast } = useToast(); - const [agentObject, setAgentObject] = useState(null); - - const form = useForm>({ - resolver: zodResolver(formSchema), - defaultValues: { - agentName: "", - agentDescription: "", - }, - }); - - const onSubmit = async (values: z.infer) => { - if (!agentObject) { - form.setError("root", { message: "No Agent object to save" }); - return; - } - - setIsLoading(true); - - const payload: GraphCreatable = { - ...agentObject, - name: values.agentName, - description: values.agentDescription, - is_active: true, - }; - - try { - const response = await api.createGraph(payload); - setIsOpen(false); - toast({ - title: "Success", - description: "Agent uploaded successfully", - variant: "default", - }); - const qID = "flowID"; - window.location.href = `/build?${qID}=${response.id}`; - } catch (error) { - form.setError("root", { - message: `Could not create agent: ${error}`, - }); - } finally { - setIsLoading(false); - } - }; - - const handleChange = (file: File) => { - setTimeout(() => { - setisDroped(false); - }, 2000); - - form.setValue("agentFile", file); - const reader = new FileReader(); - reader.onload = (event) => { - try { - const obj = JSON.parse(event.target?.result as string); - if ( - !["name", "description", "nodes", "links"].every( - (key) => key in obj && obj[key] != null, - ) - ) { - throw new Error( - "Invalid agent object in file: " + JSON.stringify(obj, null, 2), - ); - } - const agent = obj as Graph; - sanitizeImportedGraph(agent); - setAgentObject(agent); - if (!form.getValues("agentName")) { - form.setValue("agentName", agent.name); - } - if (!form.getValues("agentDescription")) { - form.setValue("agentDescription", agent.description); - } - } catch (error) { - console.error("Error loading agent file:", error); - } - }; - reader.readAsText(file); - setisDroped(false); - }; - + const { + onSubmit, + isUploading, + isOpen, + setIsOpen, + isDroped, + handleChange, + form, + setisDroped, + agentObject, + } = useLibraryUploadAgentDialog(); return ( @@ -249,9 +166,9 @@ export default function LibraryUploadAgentDialog(): React.ReactNode { type="submit" variant="primary" className="mt-2 self-end" - disabled={!agentObject || isLoading} + disabled={!agentObject || isUploading} > - {isLoading ? ( + {isUploading ? (
Uploading... diff --git a/autogpt_platform/frontend/src/app/(platform)/library/components/LibraryUploadAgentDialog/useLibraryUploadAgentDialog.ts b/autogpt_platform/frontend/src/app/(platform)/library/components/LibraryUploadAgentDialog/useLibraryUploadAgentDialog.ts new file mode 100644 index 0000000000..e0a0b90716 --- /dev/null +++ b/autogpt_platform/frontend/src/app/(platform)/library/components/LibraryUploadAgentDialog/useLibraryUploadAgentDialog.ts @@ -0,0 +1,116 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; +import { uploadAgentFormSchema } from "./LibraryUploadAgentDialog"; +import { usePostV1CreateNewGraph } from "@/app/api/__generated__/endpoints/graphs/graphs"; +import { GraphModel } from "@/app/api/__generated__/models/graphModel"; +import { useToast } from "@/components/ui/use-toast"; +import { useState } from "react"; +import { Graph } from "@/app/api/__generated__/models/graph"; +import { sanitizeImportedGraph } from "@/lib/autogpt-server-api"; + +export const useLibraryUploadAgentDialog = () => { + const [isDroped, setisDroped] = useState(false); + const [isOpen, setIsOpen] = useState(false); + const { toast } = useToast(); + const [agentObject, setAgentObject] = useState(null); + + const { mutateAsync: createGraph, isPending: isUploading } = + usePostV1CreateNewGraph({ + mutation: { + onSuccess: ({ data }) => { + setIsOpen(false); + toast({ + title: "Success", + description: "Agent uploaded successfully", + variant: "default", + }); + const qID = "flowID"; + window.location.href = `/build?${qID}=${(data as GraphModel).id}`; + }, + onError: () => { + toast({ + title: "Error", + description: "Error Uploading agent", + variant: "destructive", + }); + }, + }, + }); + + const form = useForm>({ + resolver: zodResolver(uploadAgentFormSchema), + defaultValues: { + agentName: "", + agentDescription: "", + }, + }); + + const onSubmit = async (values: z.infer) => { + if (!agentObject) { + form.setError("root", { message: "No Agent object to save" }); + return; + } + + const payload: Graph = { + ...agentObject, + name: values.agentName, + description: values.agentDescription, + is_active: true, + }; + + await createGraph({ + data: { + graph: payload, + }, + }); + }; + + const handleChange = (file: File) => { + setTimeout(() => { + setisDroped(false); + }, 2000); + + form.setValue("agentFile", file); + const reader = new FileReader(); + reader.onload = (event) => { + try { + const obj = JSON.parse(event.target?.result as string); + if ( + !["name", "description", "nodes", "links"].every( + (key) => key in obj && obj[key] != null, + ) + ) { + throw new Error( + "Invalid agent object in file: " + JSON.stringify(obj, null, 2), + ); + } + const agent = obj as Graph; + sanitizeImportedGraph(agent); + setAgentObject(agent); + if (!form.getValues("agentName")) { + form.setValue("agentName", agent.name); + } + if (!form.getValues("agentDescription")) { + form.setValue("agentDescription", agent.description); + } + } catch (error) { + console.error("Error loading agent file:", error); + } + }; + reader.readAsText(file); + setisDroped(false); + }; + + return { + onSubmit, + isUploading, + isOpen, + setIsOpen, + form, + agentObject, + isDroped, + handleChange, + setisDroped, + }; +}; diff --git a/autogpt_platform/frontend/src/app/(platform)/library/state-provider.tsx b/autogpt_platform/frontend/src/app/(platform)/library/components/state-provider.tsx similarity index 58% rename from autogpt_platform/frontend/src/app/(platform)/library/state-provider.tsx rename to autogpt_platform/frontend/src/app/(platform)/library/components/state-provider.tsx index 70ac649a3f..20afbc9cd5 100644 --- a/autogpt_platform/frontend/src/app/(platform)/library/state-provider.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/library/components/state-provider.tsx @@ -1,5 +1,6 @@ "use client"; +import { LibraryAgentSort } from "@/app/api/__generated__/models/libraryAgentSort"; import { createContext, useState, @@ -8,19 +9,14 @@ import { Dispatch, SetStateAction, } from "react"; -import { LibraryAgent, LibraryAgentSortEnum } from "@/lib/autogpt-server-api"; interface LibraryPageContextType { - agents: LibraryAgent[]; - setAgents: Dispatch>; - agentLoading: boolean; - setAgentLoading: Dispatch>; - searchTerm: string | undefined; - setSearchTerm: Dispatch>; + searchTerm: string; + setSearchTerm: Dispatch>; uploadedFile: File | null; setUploadedFile: Dispatch>; - librarySort: LibraryAgentSortEnum; - setLibrarySort: Dispatch>; + librarySort: LibraryAgentSort; + setLibrarySort: Dispatch>; } export const LibraryPageContext = createContext( @@ -32,21 +28,15 @@ export function LibraryPageStateProvider({ }: { children: ReactNode; }) { - const [agents, setAgents] = useState([]); - const [agentLoading, setAgentLoading] = useState(true); - const [searchTerm, setSearchTerm] = useState(""); + const [searchTerm, setSearchTerm] = useState(""); const [uploadedFile, setUploadedFile] = useState(null); - const [librarySort, setLibrarySort] = useState( - LibraryAgentSortEnum.UPDATED_AT, + const [librarySort, setLibrarySort] = useState( + LibraryAgentSort.updatedAt, ); return ( - {/* Header section containing notifications, search functionality and upload mechanism */} - - {/* Subheader section containing agent counts and filtering options */} - - - {/* Content section displaying agent list with counter and filtering options */} diff --git a/autogpt_platform/frontend/src/app/(platform)/login/page.tsx b/autogpt_platform/frontend/src/app/(platform)/login/page.tsx index b605cba124..4ce4aab9a9 100644 --- a/autogpt_platform/frontend/src/app/(platform)/login/page.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/login/page.tsx @@ -1,4 +1,14 @@ "use client"; +import { + AuthBottomText, + AuthButton, + AuthCard, + AuthFeedback, + AuthHeader, + GoogleOAuthButton, + PasswordInput, + Turnstile, +} from "@/components/auth"; import { Form, FormControl, @@ -8,19 +18,9 @@ import { FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; -import Link from "next/link"; import LoadingBox from "@/components/ui/loading"; -import { - AuthCard, - AuthHeader, - AuthButton, - AuthFeedback, - AuthBottomText, - GoogleOAuthButton, - PasswordInput, - Turnstile, -} from "@/components/auth"; import { getBehaveAs } from "@/lib/utils"; +import Link from "next/link"; import { useLoginPage } from "./useLoginPage"; export default function LoginPage() { @@ -100,7 +100,7 @@ export default function LoginPage() { Password Forgot your password? diff --git a/autogpt_platform/frontend/src/app/(platform)/monitoring/page.tsx b/autogpt_platform/frontend/src/app/(platform)/monitoring/page.tsx index e6c1f11efb..434103bf34 100644 --- a/autogpt_platform/frontend/src/app/(platform)/monitoring/page.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/monitoring/page.tsx @@ -32,12 +32,13 @@ const Monitor = () => { const api = useBackendAPI(); const fetchSchedules = useCallback(async () => { - setSchedules(await api.listSchedules()); + setSchedules(await api.listAllGraphsExecutionSchedules()); }, [api]); const removeSchedule = useCallback( async (scheduleId: ScheduleID) => { - const removedSchedule = await api.deleteSchedule(scheduleId); + const removedSchedule = + await api.deleteGraphExecutionSchedule(scheduleId); setSchedules(schedules.filter((s) => s.id !== removedSchedule.id)); }, [schedules, api], diff --git a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/api_keys/components/APIKeySection/APIKeySection.tsx b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/api_keys/components/APIKeySection/APIKeySection.tsx new file mode 100644 index 0000000000..a9f98d66d0 --- /dev/null +++ b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/api_keys/components/APIKeySection/APIKeySection.tsx @@ -0,0 +1,102 @@ +"use client"; + +import { Loader2, MoreVertical } from "lucide-react"; +import { Button } from "@/components/ui/button"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { Badge } from "@/components/ui/badge"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { useAPISection } from "./useAPISection"; + +export function APIKeysSection() { + const { apiKeys, isLoading, isDeleting, handleRevokeKey } = useAPISection(); + + return ( + <> + {isLoading ? ( +
+ +
+ ) : ( + apiKeys && + apiKeys.length > 0 && ( + + + + Name + API Key + Status + Created + Last Used + + + + + {apiKeys.map((key) => ( + + {key.name} + +
+ {`${key.prefix}******************${key.postfix}`} +
+
+ + + {key.status} + + + + {new Date(key.created_at).toLocaleDateString()} + + + {key.last_used_at + ? new Date(key.last_used_at).toLocaleDateString() + : "Never"} + + + + + + + + handleRevokeKey(key.id)} + disabled={isDeleting} + > + Revoke + + + + +
+ ))} +
+
+ ) + )} + + ); +} diff --git a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/api_keys/components/APIKeySection/useAPISection.tsx b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/api_keys/components/APIKeySection/useAPISection.tsx new file mode 100644 index 0000000000..3e135c8b9c --- /dev/null +++ b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/api_keys/components/APIKeySection/useAPISection.tsx @@ -0,0 +1,61 @@ +"use client"; +import { + getGetV1ListUserApiKeysQueryKey, + useDeleteV1RevokeApiKey, + useGetV1ListUserApiKeys, +} from "@/app/api/__generated__/endpoints/api-keys/api-keys"; +import { APIKeyWithoutHash } from "@/app/api/__generated__/models/aPIKeyWithoutHash"; +import { useToast } from "@/components/ui/use-toast"; +import { getQueryClient } from "@/lib/react-query/queryClient"; + +export const useAPISection = () => { + const queryClient = getQueryClient(); + const { toast } = useToast(); + + const { data: apiKeys, isLoading } = useGetV1ListUserApiKeys({ + query: { + select: (res) => { + return (res.data as APIKeyWithoutHash[]).filter( + (key) => key.status === "ACTIVE", + ); + }, + }, + }); + + const { mutateAsync: revokeAPIKey, isPending: isDeleting } = + useDeleteV1RevokeApiKey({ + mutation: { + onSettled: () => { + return queryClient.invalidateQueries({ + queryKey: getGetV1ListUserApiKeysQueryKey(), + }); + }, + }, + }); + + const handleRevokeKey = async (keyId: string) => { + try { + await revokeAPIKey({ + keyId: keyId, + }); + + toast({ + title: "Success", + description: "AutoGPT Platform API key revoked successfully", + }); + } catch { + toast({ + title: "Error", + description: "Failed to revoke AutoGPT Platform API key", + variant: "destructive", + }); + } + }; + + return { + apiKeys, + isLoading, + isDeleting, + handleRevokeKey, + }; +}; diff --git a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/api_keys/components/APIKeysModals/APIKeysModals.tsx b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/api_keys/components/APIKeysModals/APIKeysModals.tsx new file mode 100644 index 0000000000..04d6155936 --- /dev/null +++ b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/api_keys/components/APIKeysModals/APIKeysModals.tsx @@ -0,0 +1,133 @@ +"use client"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "@/components/ui/dialog"; +import { LuCopy } from "react-icons/lu"; +import { Label } from "@/components/ui/label"; +import { Input } from "@/components/ui/input"; +import { Checkbox } from "@/components/ui/checkbox"; +import { Button } from "@/components/ui/button"; + +import { useAPIkeysModals } from "./useAPIkeysModals"; +import { APIKeyPermission } from "@/app/api/__generated__/models/aPIKeyPermission"; + +export const APIKeysModals = () => { + const { + isCreating, + handleCreateKey, + handleCopyKey, + setIsCreateOpen, + setIsKeyDialogOpen, + isCreateOpen, + isKeyDialogOpen, + keyState, + setKeyState, + } = useAPIkeysModals(); + + return ( +
+ + + + + + + Create New API Key + + Create a new AutoGPT Platform API key + + +
+
+ + + setKeyState((prev) => ({ + ...prev, + newKeyName: e.target.value, + })) + } + placeholder="My AutoGPT Platform API Key" + /> +
+
+ + + setKeyState((prev) => ({ + ...prev, + newKeyDescription: e.target.value, + })) + } + placeholder="Used for..." + /> +
+
+ + {Object.values(APIKeyPermission).map((permission) => ( +
+ { + setKeyState((prev) => ({ + ...prev, + selectedPermissions: checked + ? [...prev.selectedPermissions, permission] + : prev.selectedPermissions.filter( + (p) => p !== permission, + ), + })); + }} + /> + +
+ ))} +
+
+ + + + +
+
+ + + + + AutoGPT Platform API Key Created + + Please copy your AutoGPT API key now. You won't be able to + see it again! + + +
+ + {keyState.newApiKey} + + +
+ + + +
+
+
+ ); +}; diff --git a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/api_keys/components/APIKeysModals/useAPIkeysModals.tsx b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/api_keys/components/APIKeysModals/useAPIkeysModals.tsx new file mode 100644 index 0000000000..6332205705 --- /dev/null +++ b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/api_keys/components/APIKeysModals/useAPIkeysModals.tsx @@ -0,0 +1,78 @@ +"use client"; +import { + getGetV1ListUserApiKeysQueryKey, + usePostV1CreateNewApiKey, +} from "@/app/api/__generated__/endpoints/api-keys/api-keys"; +import { APIKeyPermission } from "@/app/api/__generated__/models/aPIKeyPermission"; +import { CreateAPIKeyResponse } from "@/app/api/__generated__/models/createAPIKeyResponse"; +import { useToast } from "@/components/ui/use-toast"; +import { getQueryClient } from "@/lib/react-query/queryClient"; +import { useState } from "react"; + +export const useAPIkeysModals = () => { + const [isCreateOpen, setIsCreateOpen] = useState(false); + const [isKeyDialogOpen, setIsKeyDialogOpen] = useState(false); + const [keyState, setKeyState] = useState({ + newKeyName: "", + newKeyDescription: "", + newApiKey: "", + selectedPermissions: [] as APIKeyPermission[], + }); + const queryClient = getQueryClient(); + const { toast } = useToast(); + + const { mutateAsync: createAPIKey, isPending: isCreating } = + usePostV1CreateNewApiKey({ + mutation: { + onSettled: () => { + return queryClient.invalidateQueries({ + queryKey: getGetV1ListUserApiKeysQueryKey(), + }); + }, + }, + }); + + const handleCreateKey = async () => { + try { + const response = await createAPIKey({ + data: { + name: keyState.newKeyName, + permissions: keyState.selectedPermissions, + description: keyState.newKeyDescription, + }, + }); + setKeyState((prev) => ({ + ...prev, + newApiKey: (response.data as CreateAPIKeyResponse).plain_text_key, + })); + setIsCreateOpen(false); + setIsKeyDialogOpen(true); + } catch { + toast({ + title: "Error", + description: "Failed to create AutoGPT Platform API key", + variant: "destructive", + }); + } + }; + + const handleCopyKey = () => { + navigator.clipboard.writeText(keyState.newApiKey); + toast({ + title: "Copied", + description: "AutoGPT Platform API key copied to clipboard", + }); + }; + + return { + isCreating, + handleCreateKey, + handleCopyKey, + setIsCreateOpen, + setIsKeyDialogOpen, + isCreateOpen, + isKeyDialogOpen, + keyState, + setKeyState, + }; +}; diff --git a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/api_keys/page.tsx b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/api_keys/page.tsx index 0d1b05e882..1fa2149eec 100644 --- a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/api_keys/page.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/api_keys/page.tsx @@ -1,12 +1,31 @@ import { Metadata } from "next/types"; -import { APIKeysSection } from "@/components/agptui/composite/APIKeySection"; +import { APIKeysSection } from "@/app/(platform)/profile/(user)/api_keys/components/APIKeySection/APIKeySection"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { APIKeysModals } from "./components/APIKeysModals/APIKeysModals"; export const metadata: Metadata = { title: "API Keys - AutoGPT Platform" }; const ApiKeysPage = () => { return (
- + + + AutoGPT Platform API Keys + + Manage your AutoGPT Platform API keys for programmatic access + + + + + + +
); }; diff --git a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/integrations/page.tsx b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/integrations/page.tsx index 7ffb97ca5e..422a9b3a99 100644 --- a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/integrations/page.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/integrations/page.tsx @@ -5,6 +5,7 @@ import { useCallback, useContext, useEffect, useMemo, useState } from "react"; import { useToast } from "@/components/ui/use-toast"; import { IconKey, IconUser } from "@/components/ui/icons"; import { Trash2Icon } from "lucide-react"; +import { KeyIcon } from "@phosphor-icons/react/dist/ssr"; import { providerIcons } from "@/components/integrations/credentials-input"; import { CredentialsProvidersContext } from "@/components/integrations/credentials-provider"; import { @@ -140,11 +141,12 @@ export default function UserIntegrationsPage() { ...credentials, provider: provider.provider, providerName: provider.providerName, - ProviderIcon: providerIcons[provider.provider], + ProviderIcon: providerIcons[provider.provider] || KeyIcon, TypeIcon: { oauth2: IconUser, api_key: IconKey, user_password: IconKey, + host_scoped: IconKey, }[credentials.type], })), ) @@ -181,6 +183,7 @@ export default function UserIntegrationsPage() { oauth2: "OAuth2 credentials", api_key: "API key", user_password: "Username & password", + host_scoped: "Host-scoped credentials", }[cred.type] }{" "} - {cred.id} diff --git a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/actions.ts b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/actions.ts index 09fc2e26e0..97c9d8b153 100644 --- a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/actions.ts +++ b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/actions.ts @@ -2,8 +2,11 @@ import { revalidatePath } from "next/cache"; import { getServerSupabase } from "@/lib/supabase/server/getServerSupabase"; -import BackendApi from "@/lib/autogpt-server-api"; import { NotificationPreferenceDTO } from "@/lib/autogpt-server-api/types"; +import { + postV1UpdateNotificationPreferences, + postV1UpdateUserEmail, +} from "@/app/api/__generated__/endpoints/auth/auth"; export async function updateSettings(formData: FormData) { const supabase = await getServerSupabase(); @@ -29,8 +32,7 @@ export async function updateSettings(formData: FormData) { const { error: emailError } = await supabase.auth.updateUser({ email, }); - const api = new BackendApi(); - await api.updateUserEmail(email); + await postV1UpdateUserEmail(email); if (emailError) { throw new Error(`${emailError.message}`); @@ -38,7 +40,6 @@ export async function updateSettings(formData: FormData) { } try { - const api = new BackendApi(); const preferences: NotificationPreferenceDTO = { email: user?.email || "", preferences: { @@ -55,7 +56,7 @@ export async function updateSettings(formData: FormData) { }, daily_limit: 0, }; - await api.updateUserPreferences(preferences); + await postV1UpdateNotificationPreferences(preferences); } catch (error) { console.error(error); throw new Error(`Failed to update preferences: ${error}`); @@ -64,9 +65,3 @@ export async function updateSettings(formData: FormData) { revalidatePath("/profile/settings"); return { success: true }; } - -export async function getUserPreferences(): Promise { - const api = new BackendApi(); - const preferences = await api.getUserPreferences(); - return preferences; -} diff --git a/autogpt_platform/frontend/src/components/profile/settings/SettingsForm.tsx b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/SettingsForm.tsx similarity index 77% rename from autogpt_platform/frontend/src/components/profile/settings/SettingsForm.tsx rename to autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/SettingsForm.tsx index 8b8ca6449a..13734ad8de 100644 --- a/autogpt_platform/frontend/src/components/profile/settings/SettingsForm.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/SettingsForm.tsx @@ -1,10 +1,5 @@ "use client"; -import { zodResolver } from "@hookform/resolvers/zod"; -import { useForm } from "react-hook-form"; -import * as z from "zod"; -import { User } from "@supabase/supabase-js"; - import { Button } from "@/components/ui/button"; import { Form, @@ -18,100 +13,22 @@ import { import { Input } from "@/components/ui/input"; import { Switch } from "@/components/ui/switch"; import { Separator } from "@/components/ui/separator"; -import { updateSettings } from "@/app/(platform)/profile/(user)/settings/actions"; -import { toast } from "@/components/ui/use-toast"; -import { NotificationPreferenceDTO } from "@/lib/autogpt-server-api"; +import { NotificationPreference } from "@/app/api/__generated__/models/notificationPreference"; +import { User } from "@supabase/supabase-js"; +import { useSettingsForm } from "./useSettingsForm"; -const formSchema = z - .object({ - email: z.string().email(), - password: z - .string() - .optional() - .refine((val) => { - // If password is provided, it must be at least 8 characters - if (val) return val.length >= 12; - return true; - }, "String must contain at least 12 character(s)"), - confirmPassword: z.string().optional(), - notifyOnAgentRun: z.boolean(), - notifyOnZeroBalance: z.boolean(), - notifyOnLowBalance: z.boolean(), - notifyOnBlockExecutionFailed: z.boolean(), - notifyOnContinuousAgentError: z.boolean(), - notifyOnDailySummary: z.boolean(), - notifyOnWeeklySummary: z.boolean(), - notifyOnMonthlySummary: z.boolean(), - }) - .refine( - (data) => { - if (data.password || data.confirmPassword) { - return data.password === data.confirmPassword; - } - return true; - }, - { - message: "Passwords do not match", - path: ["confirmPassword"], - }, - ); - -interface SettingsFormProps { +export const SettingsForm = ({ + preferences, + user, +}: { + preferences: NotificationPreference; user: User; - preferences: NotificationPreferenceDTO; -} - -export default function SettingsForm({ user, preferences }: SettingsFormProps) { - const defaultValues = { - email: user.email || "", - password: "", - confirmPassword: "", - notifyOnAgentRun: preferences.preferences.AGENT_RUN, - notifyOnZeroBalance: preferences.preferences.ZERO_BALANCE, - notifyOnLowBalance: preferences.preferences.LOW_BALANCE, - notifyOnBlockExecutionFailed: - preferences.preferences.BLOCK_EXECUTION_FAILED, - notifyOnContinuousAgentError: - preferences.preferences.CONTINUOUS_AGENT_ERROR, - notifyOnDailySummary: preferences.preferences.DAILY_SUMMARY, - notifyOnWeeklySummary: preferences.preferences.WEEKLY_SUMMARY, - notifyOnMonthlySummary: preferences.preferences.MONTHLY_SUMMARY, - }; - - const form = useForm>({ - resolver: zodResolver(formSchema), - defaultValues, +}) => { + const { form, onSubmit, onCancel } = useSettingsForm({ + preferences, + user, }); - async function onSubmit(values: z.infer) { - try { - const formData = new FormData(); - - Object.entries(values).forEach(([key, value]) => { - if (key !== "confirmPassword") { - formData.append(key, value.toString()); - } - }); - - await updateSettings(formData); - - toast({ - title: "Successfully updated settings", - }); - } catch (error) { - toast({ - title: "Error", - description: - error instanceof Error ? error.message : "Something went wrong", - variant: "destructive", - }); - throw error; - } - } - - function onCancel() { - form.reset(defaultValues); - } return (
); -} +}; diff --git a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/helper.ts b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/helper.ts new file mode 100644 index 0000000000..ad04713af2 --- /dev/null +++ b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/helper.ts @@ -0,0 +1,51 @@ +import { z } from "zod"; + +export const formSchema = z + .object({ + email: z.string().email(), + password: z + .string() + .optional() + .refine((val) => { + if (val) return val.length >= 12; + return true; + }, "String must contain at least 12 character(s)"), + confirmPassword: z.string().optional(), + notifyOnAgentRun: z.boolean(), + notifyOnZeroBalance: z.boolean(), + notifyOnLowBalance: z.boolean(), + notifyOnBlockExecutionFailed: z.boolean(), + notifyOnContinuousAgentError: z.boolean(), + notifyOnDailySummary: z.boolean(), + notifyOnWeeklySummary: z.boolean(), + notifyOnMonthlySummary: z.boolean(), + }) + .refine((data) => { + if (data.password || data.confirmPassword) { + return data.password === data.confirmPassword; + } + return true; + }); + +export const createDefaultValues = ( + user: { email?: string }, + preferences: { preferences?: Record }, +) => { + const defaultValues = { + email: user.email || "", + password: "", + confirmPassword: "", + notifyOnAgentRun: preferences.preferences?.AGENT_RUN, + notifyOnZeroBalance: preferences.preferences?.ZERO_BALANCE, + notifyOnLowBalance: preferences.preferences?.LOW_BALANCE, + notifyOnBlockExecutionFailed: + preferences.preferences?.BLOCK_EXECUTION_FAILED, + notifyOnContinuousAgentError: + preferences.preferences?.CONTINUOUS_AGENT_ERROR, + notifyOnDailySummary: preferences.preferences?.DAILY_SUMMARY, + notifyOnWeeklySummary: preferences.preferences?.WEEKLY_SUMMARY, + notifyOnMonthlySummary: preferences.preferences?.MONTHLY_SUMMARY, + }; + + return defaultValues; +}; diff --git a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/useSettingsForm.tsx b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/useSettingsForm.tsx new file mode 100644 index 0000000000..4a297ee1a5 --- /dev/null +++ b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/components/SettingsForm/useSettingsForm.tsx @@ -0,0 +1,57 @@ +"use client"; +import { useForm } from "react-hook-form"; +import { createDefaultValues, formSchema } from "./helper"; +import { z } from "zod"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { updateSettings } from "../../actions"; +import { useToast } from "@/components/ui/use-toast"; +import { NotificationPreference } from "@/app/api/__generated__/models/notificationPreference"; +import { User } from "@supabase/supabase-js"; + +export const useSettingsForm = ({ + preferences, + user, +}: { + preferences: NotificationPreference; + user: User; +}) => { + const { toast } = useToast(); + const defaultValues = createDefaultValues(user, preferences); + + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues, + }); + + async function onSubmit(values: z.infer) { + try { + const formData = new FormData(); + + Object.entries(values).forEach(([key, value]) => { + if (key !== "confirmPassword") { + formData.append(key, value.toString()); + } + }); + + await updateSettings(formData); + + toast({ + title: "Successfully updated settings", + }); + } catch (error) { + toast({ + title: "Error", + description: + error instanceof Error ? error.message : "Something went wrong", + variant: "destructive", + }); + throw error; + } + } + + function onCancel() { + form.reset(defaultValues); + } + + return { form, onSubmit, onCancel }; +}; diff --git a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/page.tsx b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/page.tsx index e000accbe1..8152999b5c 100644 --- a/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/page.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/profile/(user)/settings/page.tsx @@ -1,23 +1,37 @@ +"use client"; +import { useGetV1GetNotificationPreferences } from "@/app/api/__generated__/endpoints/auth/auth"; +import { SettingsForm } from "@/app/(platform)/profile/(user)/settings/components/SettingsForm/SettingsForm"; +import { useSupabase } from "@/lib/supabase/hooks/useSupabase"; import * as React from "react"; -import { Metadata } from "next"; -import SettingsForm from "@/components/profile/settings/SettingsForm"; -import { getServerUser } from "@/lib/supabase/server/getServerUser"; +import SettingsLoading from "./loading"; import { redirect } from "next/navigation"; -import { getUserPreferences } from "./actions"; -export const metadata: Metadata = { - title: "Settings - AutoGPT Platform", - description: "Manage your account settings and preferences.", -}; +export default function SettingsPage() { + const { + data: preferences, + isError, + isLoading, + } = useGetV1GetNotificationPreferences({ + query: { + select: (res) => { + return res.data; + }, + }, + }); -export default async function SettingsPage() { - const { user, error } = await getServerUser(); + const { user, isUserLoading } = useSupabase(); - if (error || !user) { + if (isLoading || isUserLoading) { + return ; + } + + if (!user) { redirect("/login"); } - const preferences = await getUserPreferences(); + if (isError || !preferences || !preferences.preferences) { + return "Errror..."; // TODO: Will use a Error reusable components from Block Menu redesign + } return (
@@ -27,7 +41,7 @@ export default async function SettingsPage() { Manage your account settings and preferences.

- +
); } diff --git a/autogpt_platform/frontend/src/app/(platform)/reset_password/actions.ts b/autogpt_platform/frontend/src/app/(platform)/reset-password/actions.ts similarity index 91% rename from autogpt_platform/frontend/src/app/(platform)/reset_password/actions.ts rename to autogpt_platform/frontend/src/app/(platform)/reset-password/actions.ts index 7cc4da5189..327b4f86de 100644 --- a/autogpt_platform/frontend/src/app/(platform)/reset_password/actions.ts +++ b/autogpt_platform/frontend/src/app/(platform)/reset-password/actions.ts @@ -1,8 +1,8 @@ "use server"; import { getServerSupabase } from "@/lib/supabase/server/getServerSupabase"; -import { redirect } from "next/navigation"; -import * as Sentry from "@sentry/nextjs"; import { verifyTurnstileToken } from "@/lib/turnstile"; +import * as Sentry from "@sentry/nextjs"; +import { redirect } from "next/navigation"; export async function sendResetEmail(email: string, turnstileToken: string) { return await Sentry.withServerActionInstrumentation( @@ -10,7 +10,8 @@ export async function sendResetEmail(email: string, turnstileToken: string) { {}, async () => { const supabase = await getServerSupabase(); - const origin = process.env.FRONTEND_BASE_URL || "http://localhost:3000"; + const origin = + process.env.NEXT_PUBLIC_FRONTEND_BASE_URL || "http://localhost:3000"; if (!supabase) { redirect("/error"); @@ -19,14 +20,14 @@ export async function sendResetEmail(email: string, turnstileToken: string) { // Verify Turnstile token if provided const success = await verifyTurnstileToken( turnstileToken, - "reset_password", + "reset-password", ); if (!success) { return "CAPTCHA verification failed. Please try again."; } const { error } = await supabase.auth.resetPasswordForEmail(email, { - redirectTo: `${origin}/reset_password`, + redirectTo: `${origin}/reset-password`, }); if (error) { diff --git a/autogpt_platform/frontend/src/app/(platform)/reset_password/page.tsx b/autogpt_platform/frontend/src/app/(platform)/reset-password/page.tsx similarity index 99% rename from autogpt_platform/frontend/src/app/(platform)/reset_password/page.tsx rename to autogpt_platform/frontend/src/app/(platform)/reset-password/page.tsx index 5a8e04290f..304d3d452c 100644 --- a/autogpt_platform/frontend/src/app/(platform)/reset_password/page.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/reset-password/page.tsx @@ -1,9 +1,9 @@ "use client"; import { - AuthCard, - AuthHeader, AuthButton, + AuthCard, AuthFeedback, + AuthHeader, PasswordInput, Turnstile, } from "@/components/auth"; @@ -17,16 +17,16 @@ import { FormMessage, } from "@/components/ui/form"; import { Input } from "@/components/ui/input"; +import LoadingBox from "@/components/ui/loading"; +import { useTurnstile } from "@/hooks/useTurnstile"; import { useSupabase } from "@/lib/supabase/hooks/useSupabase"; -import { sendEmailFormSchema, changePasswordFormSchema } from "@/types/auth"; +import { getBehaveAs } from "@/lib/utils"; +import { changePasswordFormSchema, sendEmailFormSchema } from "@/types/auth"; import { zodResolver } from "@hookform/resolvers/zod"; import { useCallback, useState } from "react"; import { useForm } from "react-hook-form"; import { z } from "zod"; import { changePassword, sendResetEmail } from "./actions"; -import LoadingBox from "@/components/ui/loading"; -import { getBehaveAs } from "@/lib/utils"; -import { useTurnstile } from "@/hooks/useTurnstile"; export default function ResetPasswordPage() { const { supabase, user, isUserLoading } = useSupabase(); diff --git a/autogpt_platform/frontend/src/app/api/__generated__/endpoints/admin/admin.ts b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/admin/admin.ts new file mode 100644 index 0000000000..adf5fa49d5 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/admin/admin.ts @@ -0,0 +1,931 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import { useMutation, useQuery } from "@tanstack/react-query"; +import type { + DataTag, + DefinedInitialDataOptions, + DefinedUseQueryResult, + MutationFunction, + QueryClient, + QueryFunction, + QueryKey, + UndefinedInitialDataOptions, + UseMutationOptions, + UseMutationResult, + UseQueryOptions, + UseQueryResult, +} from "@tanstack/react-query"; + +import type { AddUserCreditsResponse } from "../../models/addUserCreditsResponse"; + +import type { BodyPostV2AddCreditsToUser } from "../../models/bodyPostV2AddCreditsToUser"; + +import type { GetV2GetAdminListingsHistoryParams } from "../../models/getV2GetAdminListingsHistoryParams"; + +import type { GetV2GetAllUsersHistoryParams } from "../../models/getV2GetAllUsersHistoryParams"; + +import type { HTTPValidationError } from "../../models/hTTPValidationError"; + +import type { ReviewSubmissionRequest } from "../../models/reviewSubmissionRequest"; + +import type { StoreListingsWithVersionsResponse } from "../../models/storeListingsWithVersionsResponse"; + +import type { StoreSubmission } from "../../models/storeSubmission"; + +import type { UserHistoryResponse } from "../../models/userHistoryResponse"; + +import { customMutator } from "../../../mutators/custom-mutator"; + +type SecondParameter unknown> = Parameters[1]; + +/** + * Get store listings with their version history for admins. + +This provides a consolidated view of listings with their versions, +allowing for an expandable UI in the admin dashboard. + +Args: + status: Filter by submission status (PENDING, APPROVED, REJECTED) + search: Search by name, description, or user email + page: Page number for pagination + page_size: Number of items per page + +Returns: + StoreListingsWithVersionsResponse with listings and their versions + * @summary Get Admin Listings History + */ +export type getV2GetAdminListingsHistoryResponse200 = { + data: StoreListingsWithVersionsResponse; + status: 200; +}; + +export type getV2GetAdminListingsHistoryResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type getV2GetAdminListingsHistoryResponseComposite = + | getV2GetAdminListingsHistoryResponse200 + | getV2GetAdminListingsHistoryResponse422; + +export type getV2GetAdminListingsHistoryResponse = + getV2GetAdminListingsHistoryResponseComposite & { + headers: Headers; + }; + +export const getGetV2GetAdminListingsHistoryUrl = ( + params?: GetV2GetAdminListingsHistoryParams, +) => { + const normalizedParams = new URLSearchParams(); + + Object.entries(params || {}).forEach(([key, value]) => { + if (value !== undefined) { + normalizedParams.append(key, value === null ? "null" : value.toString()); + } + }); + + const stringifiedParams = normalizedParams.toString(); + + return stringifiedParams.length > 0 + ? `/api/store/admin/listings?${stringifiedParams}` + : `/api/store/admin/listings`; +}; + +export const getV2GetAdminListingsHistory = async ( + params?: GetV2GetAdminListingsHistoryParams, + options?: RequestInit, +): Promise => { + return customMutator( + getGetV2GetAdminListingsHistoryUrl(params), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV2GetAdminListingsHistoryQueryKey = ( + params?: GetV2GetAdminListingsHistoryParams, +) => { + return [`/api/store/admin/listings`, ...(params ? [params] : [])] as const; +}; + +export const getGetV2GetAdminListingsHistoryQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: GetV2GetAdminListingsHistoryParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetV2GetAdminListingsHistoryQueryKey(params); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV2GetAdminListingsHistory(params, { signal, ...requestOptions }); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV2GetAdminListingsHistoryQueryResult = NonNullable< + Awaited> +>; +export type GetV2GetAdminListingsHistoryQueryError = HTTPValidationError; + +export function useGetV2GetAdminListingsHistory< + TData = Awaited>, + TError = HTTPValidationError, +>( + params: undefined | GetV2GetAdminListingsHistoryParams, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2GetAdminListingsHistory< + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: GetV2GetAdminListingsHistoryParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2GetAdminListingsHistory< + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: GetV2GetAdminListingsHistoryParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Get Admin Listings History + */ + +export function useGetV2GetAdminListingsHistory< + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: GetV2GetAdminListingsHistoryParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV2GetAdminListingsHistoryQueryOptions( + params, + options, + ); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * Review a store listing submission. + +Args: + store_listing_version_id: ID of the submission to review + request: Review details including approval status and comments + user: Authenticated admin user performing the review + +Returns: + StoreSubmission with updated review information + * @summary Review Store Submission + */ +export type postV2ReviewStoreSubmissionResponse200 = { + data: StoreSubmission; + status: 200; +}; + +export type postV2ReviewStoreSubmissionResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV2ReviewStoreSubmissionResponseComposite = + | postV2ReviewStoreSubmissionResponse200 + | postV2ReviewStoreSubmissionResponse422; + +export type postV2ReviewStoreSubmissionResponse = + postV2ReviewStoreSubmissionResponseComposite & { + headers: Headers; + }; + +export const getPostV2ReviewStoreSubmissionUrl = ( + storeListingVersionId: string, +) => { + return `/api/store/admin/submissions/${storeListingVersionId}/review`; +}; + +export const postV2ReviewStoreSubmission = async ( + storeListingVersionId: string, + reviewSubmissionRequest: ReviewSubmissionRequest, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV2ReviewStoreSubmissionUrl(storeListingVersionId), + { + ...options, + method: "POST", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(reviewSubmissionRequest), + }, + ); +}; + +export const getPostV2ReviewStoreSubmissionMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { storeListingVersionId: string; data: ReviewSubmissionRequest }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { storeListingVersionId: string; data: ReviewSubmissionRequest }, + TContext +> => { + const mutationKey = ["postV2ReviewStoreSubmission"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { storeListingVersionId: string; data: ReviewSubmissionRequest } + > = (props) => { + const { storeListingVersionId, data } = props ?? {}; + + return postV2ReviewStoreSubmission( + storeListingVersionId, + data, + requestOptions, + ); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV2ReviewStoreSubmissionMutationResult = NonNullable< + Awaited> +>; +export type PostV2ReviewStoreSubmissionMutationBody = ReviewSubmissionRequest; +export type PostV2ReviewStoreSubmissionMutationError = HTTPValidationError; + +/** + * @summary Review Store Submission + */ +export const usePostV2ReviewStoreSubmission = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { storeListingVersionId: string; data: ReviewSubmissionRequest }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { storeListingVersionId: string; data: ReviewSubmissionRequest }, + TContext +> => { + const mutationOptions = + getPostV2ReviewStoreSubmissionMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * Download the agent file by streaming its content. + +Args: + store_listing_version_id (str): The ID of the agent to download + +Returns: + StreamingResponse: A streaming response containing the agent's graph data. + +Raises: + HTTPException: If the agent is not found or an unexpected error occurs. + * @summary Admin Download Agent File + */ +export type getV2AdminDownloadAgentFileResponse200 = { + data: unknown; + status: 200; +}; + +export type getV2AdminDownloadAgentFileResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type getV2AdminDownloadAgentFileResponseComposite = + | getV2AdminDownloadAgentFileResponse200 + | getV2AdminDownloadAgentFileResponse422; + +export type getV2AdminDownloadAgentFileResponse = + getV2AdminDownloadAgentFileResponseComposite & { + headers: Headers; + }; + +export const getGetV2AdminDownloadAgentFileUrl = ( + storeListingVersionId: string, +) => { + return `/api/store/admin/submissions/download/${storeListingVersionId}`; +}; + +export const getV2AdminDownloadAgentFile = async ( + storeListingVersionId: string, + options?: RequestInit, +): Promise => { + return customMutator( + getGetV2AdminDownloadAgentFileUrl(storeListingVersionId), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV2AdminDownloadAgentFileQueryKey = ( + storeListingVersionId: string, +) => { + return [ + `/api/store/admin/submissions/download/${storeListingVersionId}`, + ] as const; +}; + +export const getGetV2AdminDownloadAgentFileQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + storeListingVersionId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? + getGetV2AdminDownloadAgentFileQueryKey(storeListingVersionId); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV2AdminDownloadAgentFile(storeListingVersionId, { + signal, + ...requestOptions, + }); + + return { + queryKey, + queryFn, + enabled: !!storeListingVersionId, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV2AdminDownloadAgentFileQueryResult = NonNullable< + Awaited> +>; +export type GetV2AdminDownloadAgentFileQueryError = HTTPValidationError; + +export function useGetV2AdminDownloadAgentFile< + TData = Awaited>, + TError = HTTPValidationError, +>( + storeListingVersionId: string, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2AdminDownloadAgentFile< + TData = Awaited>, + TError = HTTPValidationError, +>( + storeListingVersionId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2AdminDownloadAgentFile< + TData = Awaited>, + TError = HTTPValidationError, +>( + storeListingVersionId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Admin Download Agent File + */ + +export function useGetV2AdminDownloadAgentFile< + TData = Awaited>, + TError = HTTPValidationError, +>( + storeListingVersionId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV2AdminDownloadAgentFileQueryOptions( + storeListingVersionId, + options, + ); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Add Credits to User + */ +export type postV2AddCreditsToUserResponse200 = { + data: AddUserCreditsResponse; + status: 200; +}; + +export type postV2AddCreditsToUserResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV2AddCreditsToUserResponseComposite = + | postV2AddCreditsToUserResponse200 + | postV2AddCreditsToUserResponse422; + +export type postV2AddCreditsToUserResponse = + postV2AddCreditsToUserResponseComposite & { + headers: Headers; + }; + +export const getPostV2AddCreditsToUserUrl = () => { + return `/api/credits/admin/add_credits`; +}; + +export const postV2AddCreditsToUser = async ( + bodyPostV2AddCreditsToUser: BodyPostV2AddCreditsToUser, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV2AddCreditsToUserUrl(), + { + ...options, + method: "POST", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(bodyPostV2AddCreditsToUser), + }, + ); +}; + +export const getPostV2AddCreditsToUserMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: BodyPostV2AddCreditsToUser }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { data: BodyPostV2AddCreditsToUser }, + TContext +> => { + const mutationKey = ["postV2AddCreditsToUser"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { data: BodyPostV2AddCreditsToUser } + > = (props) => { + const { data } = props ?? {}; + + return postV2AddCreditsToUser(data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV2AddCreditsToUserMutationResult = NonNullable< + Awaited> +>; +export type PostV2AddCreditsToUserMutationBody = BodyPostV2AddCreditsToUser; +export type PostV2AddCreditsToUserMutationError = HTTPValidationError; + +/** + * @summary Add Credits to User + */ +export const usePostV2AddCreditsToUser = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: BodyPostV2AddCreditsToUser }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { data: BodyPostV2AddCreditsToUser }, + TContext +> => { + const mutationOptions = getPostV2AddCreditsToUserMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * @summary Get All Users History + */ +export type getV2GetAllUsersHistoryResponse200 = { + data: UserHistoryResponse; + status: 200; +}; + +export type getV2GetAllUsersHistoryResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type getV2GetAllUsersHistoryResponseComposite = + | getV2GetAllUsersHistoryResponse200 + | getV2GetAllUsersHistoryResponse422; + +export type getV2GetAllUsersHistoryResponse = + getV2GetAllUsersHistoryResponseComposite & { + headers: Headers; + }; + +export const getGetV2GetAllUsersHistoryUrl = ( + params?: GetV2GetAllUsersHistoryParams, +) => { + const normalizedParams = new URLSearchParams(); + + Object.entries(params || {}).forEach(([key, value]) => { + if (value !== undefined) { + normalizedParams.append(key, value === null ? "null" : value.toString()); + } + }); + + const stringifiedParams = normalizedParams.toString(); + + return stringifiedParams.length > 0 + ? `/api/credits/admin/users_history?${stringifiedParams}` + : `/api/credits/admin/users_history`; +}; + +export const getV2GetAllUsersHistory = async ( + params?: GetV2GetAllUsersHistoryParams, + options?: RequestInit, +): Promise => { + return customMutator( + getGetV2GetAllUsersHistoryUrl(params), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV2GetAllUsersHistoryQueryKey = ( + params?: GetV2GetAllUsersHistoryParams, +) => { + return [ + `/api/credits/admin/users_history`, + ...(params ? [params] : []), + ] as const; +}; + +export const getGetV2GetAllUsersHistoryQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: GetV2GetAllUsersHistoryParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetV2GetAllUsersHistoryQueryKey(params); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV2GetAllUsersHistory(params, { signal, ...requestOptions }); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV2GetAllUsersHistoryQueryResult = NonNullable< + Awaited> +>; +export type GetV2GetAllUsersHistoryQueryError = HTTPValidationError; + +export function useGetV2GetAllUsersHistory< + TData = Awaited>, + TError = HTTPValidationError, +>( + params: undefined | GetV2GetAllUsersHistoryParams, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2GetAllUsersHistory< + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: GetV2GetAllUsersHistoryParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2GetAllUsersHistory< + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: GetV2GetAllUsersHistoryParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Get All Users History + */ + +export function useGetV2GetAllUsersHistory< + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: GetV2GetAllUsersHistoryParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV2GetAllUsersHistoryQueryOptions(params, options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/endpoints/analytics/analytics.ts b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/analytics/analytics.ts new file mode 100644 index 0000000000..a297a7b0b7 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/analytics/analytics.ts @@ -0,0 +1,245 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import { useMutation } from "@tanstack/react-query"; +import type { + MutationFunction, + QueryClient, + UseMutationOptions, + UseMutationResult, +} from "@tanstack/react-query"; + +import type { BodyPostV1LogRawAnalytics } from "../../models/bodyPostV1LogRawAnalytics"; + +import type { HTTPValidationError } from "../../models/hTTPValidationError"; + +import type { LogRawMetricRequest } from "../../models/logRawMetricRequest"; + +import { customMutator } from "../../../mutators/custom-mutator"; + +type SecondParameter unknown> = Parameters[1]; + +/** + * @summary Log Raw Metric + */ +export type postV1LogRawMetricResponse200 = { + data: unknown; + status: 200; +}; + +export type postV1LogRawMetricResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV1LogRawMetricResponseComposite = + | postV1LogRawMetricResponse200 + | postV1LogRawMetricResponse422; + +export type postV1LogRawMetricResponse = postV1LogRawMetricResponseComposite & { + headers: Headers; +}; + +export const getPostV1LogRawMetricUrl = () => { + return `/api/analytics/log_raw_metric`; +}; + +export const postV1LogRawMetric = async ( + logRawMetricRequest: LogRawMetricRequest, + options?: RequestInit, +): Promise => { + return customMutator(getPostV1LogRawMetricUrl(), { + ...options, + method: "POST", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(logRawMetricRequest), + }); +}; + +export const getPostV1LogRawMetricMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: LogRawMetricRequest }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { data: LogRawMetricRequest }, + TContext +> => { + const mutationKey = ["postV1LogRawMetric"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { data: LogRawMetricRequest } + > = (props) => { + const { data } = props ?? {}; + + return postV1LogRawMetric(data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV1LogRawMetricMutationResult = NonNullable< + Awaited> +>; +export type PostV1LogRawMetricMutationBody = LogRawMetricRequest; +export type PostV1LogRawMetricMutationError = HTTPValidationError; + +/** + * @summary Log Raw Metric + */ +export const usePostV1LogRawMetric = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: LogRawMetricRequest }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { data: LogRawMetricRequest }, + TContext +> => { + const mutationOptions = getPostV1LogRawMetricMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * @summary Log Raw Analytics + */ +export type postV1LogRawAnalyticsResponse200 = { + data: unknown; + status: 200; +}; + +export type postV1LogRawAnalyticsResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV1LogRawAnalyticsResponseComposite = + | postV1LogRawAnalyticsResponse200 + | postV1LogRawAnalyticsResponse422; + +export type postV1LogRawAnalyticsResponse = + postV1LogRawAnalyticsResponseComposite & { + headers: Headers; + }; + +export const getPostV1LogRawAnalyticsUrl = () => { + return `/api/analytics/log_raw_analytics`; +}; + +export const postV1LogRawAnalytics = async ( + bodyPostV1LogRawAnalytics: BodyPostV1LogRawAnalytics, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV1LogRawAnalyticsUrl(), + { + ...options, + method: "POST", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(bodyPostV1LogRawAnalytics), + }, + ); +}; + +export const getPostV1LogRawAnalyticsMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: BodyPostV1LogRawAnalytics }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { data: BodyPostV1LogRawAnalytics }, + TContext +> => { + const mutationKey = ["postV1LogRawAnalytics"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { data: BodyPostV1LogRawAnalytics } + > = (props) => { + const { data } = props ?? {}; + + return postV1LogRawAnalytics(data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV1LogRawAnalyticsMutationResult = NonNullable< + Awaited> +>; +export type PostV1LogRawAnalyticsMutationBody = BodyPostV1LogRawAnalytics; +export type PostV1LogRawAnalyticsMutationError = HTTPValidationError; + +/** + * @summary Log Raw Analytics + */ +export const usePostV1LogRawAnalytics = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: BodyPostV1LogRawAnalytics }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { data: BodyPostV1LogRawAnalytics }, + TContext +> => { + const mutationOptions = getPostV1LogRawAnalyticsMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/endpoints/api-keys/api-keys.ts b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/api-keys/api-keys.ts new file mode 100644 index 0000000000..9547793c15 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/api-keys/api-keys.ts @@ -0,0 +1,857 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import { useMutation, useQuery } from "@tanstack/react-query"; +import type { + DataTag, + DefinedInitialDataOptions, + DefinedUseQueryResult, + MutationFunction, + QueryClient, + QueryFunction, + QueryKey, + UndefinedInitialDataOptions, + UseMutationOptions, + UseMutationResult, + UseQueryOptions, + UseQueryResult, +} from "@tanstack/react-query"; + +import type { APIKeyWithoutHash } from "../../models/aPIKeyWithoutHash"; + +import type { CreateAPIKeyRequest } from "../../models/createAPIKeyRequest"; + +import type { CreateAPIKeyResponse } from "../../models/createAPIKeyResponse"; + +import type { GetV1ListUserApiKeys200 } from "../../models/getV1ListUserApiKeys200"; + +import type { HTTPValidationError } from "../../models/hTTPValidationError"; + +import type { UpdatePermissionsRequest } from "../../models/updatePermissionsRequest"; + +import { customMutator } from "../../../mutators/custom-mutator"; + +type SecondParameter unknown> = Parameters[1]; + +/** + * List all API keys for the user + * @summary List user API keys + */ +export type getV1ListUserApiKeysResponse200 = { + data: GetV1ListUserApiKeys200; + status: 200; +}; + +export type getV1ListUserApiKeysResponseComposite = + getV1ListUserApiKeysResponse200; + +export type getV1ListUserApiKeysResponse = + getV1ListUserApiKeysResponseComposite & { + headers: Headers; + }; + +export const getGetV1ListUserApiKeysUrl = () => { + return `/api/api-keys`; +}; + +export const getV1ListUserApiKeys = async ( + options?: RequestInit, +): Promise => { + return customMutator( + getGetV1ListUserApiKeysUrl(), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV1ListUserApiKeysQueryKey = () => { + return [`/api/api-keys`] as const; +}; + +export const getGetV1ListUserApiKeysQueryOptions = < + TData = Awaited>, + TError = unknown, +>(options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; +}) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getGetV1ListUserApiKeysQueryKey(); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => getV1ListUserApiKeys({ signal, ...requestOptions }); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV1ListUserApiKeysQueryResult = NonNullable< + Awaited> +>; +export type GetV1ListUserApiKeysQueryError = unknown; + +export function useGetV1ListUserApiKeys< + TData = Awaited>, + TError = unknown, +>( + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1ListUserApiKeys< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1ListUserApiKeys< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary List user API keys + */ + +export function useGetV1ListUserApiKeys< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV1ListUserApiKeysQueryOptions(options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * Create a new API key + * @summary Create new API key + */ +export type postV1CreateNewApiKeyResponse200 = { + data: CreateAPIKeyResponse; + status: 200; +}; + +export type postV1CreateNewApiKeyResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV1CreateNewApiKeyResponseComposite = + | postV1CreateNewApiKeyResponse200 + | postV1CreateNewApiKeyResponse422; + +export type postV1CreateNewApiKeyResponse = + postV1CreateNewApiKeyResponseComposite & { + headers: Headers; + }; + +export const getPostV1CreateNewApiKeyUrl = () => { + return `/api/api-keys`; +}; + +export const postV1CreateNewApiKey = async ( + createAPIKeyRequest: CreateAPIKeyRequest, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV1CreateNewApiKeyUrl(), + { + ...options, + method: "POST", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(createAPIKeyRequest), + }, + ); +}; + +export const getPostV1CreateNewApiKeyMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: CreateAPIKeyRequest }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { data: CreateAPIKeyRequest }, + TContext +> => { + const mutationKey = ["postV1CreateNewApiKey"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { data: CreateAPIKeyRequest } + > = (props) => { + const { data } = props ?? {}; + + return postV1CreateNewApiKey(data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV1CreateNewApiKeyMutationResult = NonNullable< + Awaited> +>; +export type PostV1CreateNewApiKeyMutationBody = CreateAPIKeyRequest; +export type PostV1CreateNewApiKeyMutationError = HTTPValidationError; + +/** + * @summary Create new API key + */ +export const usePostV1CreateNewApiKey = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: CreateAPIKeyRequest }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { data: CreateAPIKeyRequest }, + TContext +> => { + const mutationOptions = getPostV1CreateNewApiKeyMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * Get a specific API key + * @summary Get specific API key + */ +export type getV1GetSpecificApiKeyResponse200 = { + data: APIKeyWithoutHash; + status: 200; +}; + +export type getV1GetSpecificApiKeyResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type getV1GetSpecificApiKeyResponseComposite = + | getV1GetSpecificApiKeyResponse200 + | getV1GetSpecificApiKeyResponse422; + +export type getV1GetSpecificApiKeyResponse = + getV1GetSpecificApiKeyResponseComposite & { + headers: Headers; + }; + +export const getGetV1GetSpecificApiKeyUrl = (keyId: string) => { + return `/api/api-keys/${keyId}`; +}; + +export const getV1GetSpecificApiKey = async ( + keyId: string, + options?: RequestInit, +): Promise => { + return customMutator( + getGetV1GetSpecificApiKeyUrl(keyId), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV1GetSpecificApiKeyQueryKey = (keyId: string) => { + return [`/api/api-keys/${keyId}`] as const; +}; + +export const getGetV1GetSpecificApiKeyQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + keyId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetV1GetSpecificApiKeyQueryKey(keyId); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV1GetSpecificApiKey(keyId, { signal, ...requestOptions }); + + return { + queryKey, + queryFn, + enabled: !!keyId, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV1GetSpecificApiKeyQueryResult = NonNullable< + Awaited> +>; +export type GetV1GetSpecificApiKeyQueryError = HTTPValidationError; + +export function useGetV1GetSpecificApiKey< + TData = Awaited>, + TError = HTTPValidationError, +>( + keyId: string, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetSpecificApiKey< + TData = Awaited>, + TError = HTTPValidationError, +>( + keyId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetSpecificApiKey< + TData = Awaited>, + TError = HTTPValidationError, +>( + keyId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Get specific API key + */ + +export function useGetV1GetSpecificApiKey< + TData = Awaited>, + TError = HTTPValidationError, +>( + keyId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV1GetSpecificApiKeyQueryOptions(keyId, options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * Revoke an API key + * @summary Revoke API key + */ +export type deleteV1RevokeApiKeyResponse200 = { + data: APIKeyWithoutHash; + status: 200; +}; + +export type deleteV1RevokeApiKeyResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type deleteV1RevokeApiKeyResponseComposite = + | deleteV1RevokeApiKeyResponse200 + | deleteV1RevokeApiKeyResponse422; + +export type deleteV1RevokeApiKeyResponse = + deleteV1RevokeApiKeyResponseComposite & { + headers: Headers; + }; + +export const getDeleteV1RevokeApiKeyUrl = (keyId: string) => { + return `/api/api-keys/${keyId}`; +}; + +export const deleteV1RevokeApiKey = async ( + keyId: string, + options?: RequestInit, +): Promise => { + return customMutator( + getDeleteV1RevokeApiKeyUrl(keyId), + { + ...options, + method: "DELETE", + }, + ); +}; + +export const getDeleteV1RevokeApiKeyMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { keyId: string }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { keyId: string }, + TContext +> => { + const mutationKey = ["deleteV1RevokeApiKey"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { keyId: string } + > = (props) => { + const { keyId } = props ?? {}; + + return deleteV1RevokeApiKey(keyId, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type DeleteV1RevokeApiKeyMutationResult = NonNullable< + Awaited> +>; + +export type DeleteV1RevokeApiKeyMutationError = HTTPValidationError; + +/** + * @summary Revoke API key + */ +export const useDeleteV1RevokeApiKey = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { keyId: string }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { keyId: string }, + TContext +> => { + const mutationOptions = getDeleteV1RevokeApiKeyMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * Suspend an API key + * @summary Suspend API key + */ +export type postV1SuspendApiKeyResponse200 = { + data: APIKeyWithoutHash; + status: 200; +}; + +export type postV1SuspendApiKeyResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV1SuspendApiKeyResponseComposite = + | postV1SuspendApiKeyResponse200 + | postV1SuspendApiKeyResponse422; + +export type postV1SuspendApiKeyResponse = + postV1SuspendApiKeyResponseComposite & { + headers: Headers; + }; + +export const getPostV1SuspendApiKeyUrl = (keyId: string) => { + return `/api/api-keys/${keyId}/suspend`; +}; + +export const postV1SuspendApiKey = async ( + keyId: string, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV1SuspendApiKeyUrl(keyId), + { + ...options, + method: "POST", + }, + ); +}; + +export const getPostV1SuspendApiKeyMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { keyId: string }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { keyId: string }, + TContext +> => { + const mutationKey = ["postV1SuspendApiKey"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { keyId: string } + > = (props) => { + const { keyId } = props ?? {}; + + return postV1SuspendApiKey(keyId, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV1SuspendApiKeyMutationResult = NonNullable< + Awaited> +>; + +export type PostV1SuspendApiKeyMutationError = HTTPValidationError; + +/** + * @summary Suspend API key + */ +export const usePostV1SuspendApiKey = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { keyId: string }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { keyId: string }, + TContext +> => { + const mutationOptions = getPostV1SuspendApiKeyMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * Update API key permissions + * @summary Update key permissions + */ +export type putV1UpdateKeyPermissionsResponse200 = { + data: APIKeyWithoutHash; + status: 200; +}; + +export type putV1UpdateKeyPermissionsResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type putV1UpdateKeyPermissionsResponseComposite = + | putV1UpdateKeyPermissionsResponse200 + | putV1UpdateKeyPermissionsResponse422; + +export type putV1UpdateKeyPermissionsResponse = + putV1UpdateKeyPermissionsResponseComposite & { + headers: Headers; + }; + +export const getPutV1UpdateKeyPermissionsUrl = (keyId: string) => { + return `/api/api-keys/${keyId}/permissions`; +}; + +export const putV1UpdateKeyPermissions = async ( + keyId: string, + updatePermissionsRequest: UpdatePermissionsRequest, + options?: RequestInit, +): Promise => { + return customMutator( + getPutV1UpdateKeyPermissionsUrl(keyId), + { + ...options, + method: "PUT", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(updatePermissionsRequest), + }, + ); +}; + +export const getPutV1UpdateKeyPermissionsMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { keyId: string; data: UpdatePermissionsRequest }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { keyId: string; data: UpdatePermissionsRequest }, + TContext +> => { + const mutationKey = ["putV1UpdateKeyPermissions"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { keyId: string; data: UpdatePermissionsRequest } + > = (props) => { + const { keyId, data } = props ?? {}; + + return putV1UpdateKeyPermissions(keyId, data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PutV1UpdateKeyPermissionsMutationResult = NonNullable< + Awaited> +>; +export type PutV1UpdateKeyPermissionsMutationBody = UpdatePermissionsRequest; +export type PutV1UpdateKeyPermissionsMutationError = HTTPValidationError; + +/** + * @summary Update key permissions + */ +export const usePutV1UpdateKeyPermissions = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { keyId: string; data: UpdatePermissionsRequest }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { keyId: string; data: UpdatePermissionsRequest }, + TContext +> => { + const mutationOptions = getPutV1UpdateKeyPermissionsMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/endpoints/auth/auth.ts b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/auth/auth.ts new file mode 100644 index 0000000000..711bfb2cd2 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/auth/auth.ts @@ -0,0 +1,535 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import { useMutation, useQuery } from "@tanstack/react-query"; +import type { + DataTag, + DefinedInitialDataOptions, + DefinedUseQueryResult, + MutationFunction, + QueryClient, + QueryFunction, + QueryKey, + UndefinedInitialDataOptions, + UseMutationOptions, + UseMutationResult, + UseQueryOptions, + UseQueryResult, +} from "@tanstack/react-query"; + +import type { HTTPValidationError } from "../../models/hTTPValidationError"; + +import type { NotificationPreference } from "../../models/notificationPreference"; + +import type { NotificationPreferenceDTO } from "../../models/notificationPreferenceDTO"; + +import type { PostV1UpdateUserEmail200 } from "../../models/postV1UpdateUserEmail200"; + +import { customMutator } from "../../../mutators/custom-mutator"; + +type SecondParameter unknown> = Parameters[1]; + +/** + * @summary Get or create user + */ +export type postV1GetOrCreateUserResponse200 = { + data: unknown; + status: 200; +}; + +export type postV1GetOrCreateUserResponseComposite = + postV1GetOrCreateUserResponse200; + +export type postV1GetOrCreateUserResponse = + postV1GetOrCreateUserResponseComposite & { + headers: Headers; + }; + +export const getPostV1GetOrCreateUserUrl = () => { + return `/api/auth/user`; +}; + +export const postV1GetOrCreateUser = async ( + options?: RequestInit, +): Promise => { + return customMutator( + getPostV1GetOrCreateUserUrl(), + { + ...options, + method: "POST", + }, + ); +}; + +export const getPostV1GetOrCreateUserMutationOptions = < + TError = unknown, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + void, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + void, + TContext +> => { + const mutationKey = ["postV1GetOrCreateUser"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + void + > = () => { + return postV1GetOrCreateUser(requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV1GetOrCreateUserMutationResult = NonNullable< + Awaited> +>; + +export type PostV1GetOrCreateUserMutationError = unknown; + +/** + * @summary Get or create user + */ +export const usePostV1GetOrCreateUser = ( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + void, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + void, + TContext +> => { + const mutationOptions = getPostV1GetOrCreateUserMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * @summary Update user email + */ +export type postV1UpdateUserEmailResponse200 = { + data: PostV1UpdateUserEmail200; + status: 200; +}; + +export type postV1UpdateUserEmailResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV1UpdateUserEmailResponseComposite = + | postV1UpdateUserEmailResponse200 + | postV1UpdateUserEmailResponse422; + +export type postV1UpdateUserEmailResponse = + postV1UpdateUserEmailResponseComposite & { + headers: Headers; + }; + +export const getPostV1UpdateUserEmailUrl = () => { + return `/api/auth/user/email`; +}; + +export const postV1UpdateUserEmail = async ( + postV1UpdateUserEmailBody: string, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV1UpdateUserEmailUrl(), + { + ...options, + method: "POST", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(postV1UpdateUserEmailBody), + }, + ); +}; + +export const getPostV1UpdateUserEmailMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: string }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { data: string }, + TContext +> => { + const mutationKey = ["postV1UpdateUserEmail"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { data: string } + > = (props) => { + const { data } = props ?? {}; + + return postV1UpdateUserEmail(data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV1UpdateUserEmailMutationResult = NonNullable< + Awaited> +>; +export type PostV1UpdateUserEmailMutationBody = string; +export type PostV1UpdateUserEmailMutationError = HTTPValidationError; + +/** + * @summary Update user email + */ +export const usePostV1UpdateUserEmail = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: string }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { data: string }, + TContext +> => { + const mutationOptions = getPostV1UpdateUserEmailMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * @summary Get notification preferences + */ +export type getV1GetNotificationPreferencesResponse200 = { + data: NotificationPreference; + status: 200; +}; + +export type getV1GetNotificationPreferencesResponseComposite = + getV1GetNotificationPreferencesResponse200; + +export type getV1GetNotificationPreferencesResponse = + getV1GetNotificationPreferencesResponseComposite & { + headers: Headers; + }; + +export const getGetV1GetNotificationPreferencesUrl = () => { + return `/api/auth/user/preferences`; +}; + +export const getV1GetNotificationPreferences = async ( + options?: RequestInit, +): Promise => { + return customMutator( + getGetV1GetNotificationPreferencesUrl(), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV1GetNotificationPreferencesQueryKey = () => { + return [`/api/auth/user/preferences`] as const; +}; + +export const getGetV1GetNotificationPreferencesQueryOptions = < + TData = Awaited>, + TError = unknown, +>(options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; +}) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetV1GetNotificationPreferencesQueryKey(); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV1GetNotificationPreferences({ signal, ...requestOptions }); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV1GetNotificationPreferencesQueryResult = NonNullable< + Awaited> +>; +export type GetV1GetNotificationPreferencesQueryError = unknown; + +export function useGetV1GetNotificationPreferences< + TData = Awaited>, + TError = unknown, +>( + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetNotificationPreferences< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetNotificationPreferences< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Get notification preferences + */ + +export function useGetV1GetNotificationPreferences< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV1GetNotificationPreferencesQueryOptions(options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Update notification preferences + */ +export type postV1UpdateNotificationPreferencesResponse200 = { + data: NotificationPreference; + status: 200; +}; + +export type postV1UpdateNotificationPreferencesResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV1UpdateNotificationPreferencesResponseComposite = + | postV1UpdateNotificationPreferencesResponse200 + | postV1UpdateNotificationPreferencesResponse422; + +export type postV1UpdateNotificationPreferencesResponse = + postV1UpdateNotificationPreferencesResponseComposite & { + headers: Headers; + }; + +export const getPostV1UpdateNotificationPreferencesUrl = () => { + return `/api/auth/user/preferences`; +}; + +export const postV1UpdateNotificationPreferences = async ( + notificationPreferenceDTO: NotificationPreferenceDTO, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV1UpdateNotificationPreferencesUrl(), + { + ...options, + method: "POST", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(notificationPreferenceDTO), + }, + ); +}; + +export const getPostV1UpdateNotificationPreferencesMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: NotificationPreferenceDTO }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { data: NotificationPreferenceDTO }, + TContext +> => { + const mutationKey = ["postV1UpdateNotificationPreferences"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { data: NotificationPreferenceDTO } + > = (props) => { + const { data } = props ?? {}; + + return postV1UpdateNotificationPreferences(data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV1UpdateNotificationPreferencesMutationResult = NonNullable< + Awaited> +>; +export type PostV1UpdateNotificationPreferencesMutationBody = + NotificationPreferenceDTO; +export type PostV1UpdateNotificationPreferencesMutationError = + HTTPValidationError; + +/** + * @summary Update notification preferences + */ +export const usePostV1UpdateNotificationPreferences = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: NotificationPreferenceDTO }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { data: NotificationPreferenceDTO }, + TContext +> => { + const mutationOptions = + getPostV1UpdateNotificationPreferencesMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/endpoints/blocks/blocks.ts b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/blocks/blocks.ts new file mode 100644 index 0000000000..a11fb83730 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/blocks/blocks.ts @@ -0,0 +1,322 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import { useMutation, useQuery } from "@tanstack/react-query"; +import type { + DataTag, + DefinedInitialDataOptions, + DefinedUseQueryResult, + MutationFunction, + QueryClient, + QueryFunction, + QueryKey, + UndefinedInitialDataOptions, + UseMutationOptions, + UseMutationResult, + UseQueryOptions, + UseQueryResult, +} from "@tanstack/react-query"; + +import type { GetV1ListAvailableBlocks200Item } from "../../models/getV1ListAvailableBlocks200Item"; + +import type { HTTPValidationError } from "../../models/hTTPValidationError"; + +import type { PostV1ExecuteGraphBlock200 } from "../../models/postV1ExecuteGraphBlock200"; + +import type { PostV1ExecuteGraphBlockBody } from "../../models/postV1ExecuteGraphBlockBody"; + +import { customMutator } from "../../../mutators/custom-mutator"; + +type SecondParameter unknown> = Parameters[1]; + +/** + * @summary List available blocks + */ +export type getV1ListAvailableBlocksResponse200 = { + data: GetV1ListAvailableBlocks200Item[]; + status: 200; +}; + +export type getV1ListAvailableBlocksResponseComposite = + getV1ListAvailableBlocksResponse200; + +export type getV1ListAvailableBlocksResponse = + getV1ListAvailableBlocksResponseComposite & { + headers: Headers; + }; + +export const getGetV1ListAvailableBlocksUrl = () => { + return `/api/blocks`; +}; + +export const getV1ListAvailableBlocks = async ( + options?: RequestInit, +): Promise => { + return customMutator( + getGetV1ListAvailableBlocksUrl(), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV1ListAvailableBlocksQueryKey = () => { + return [`/api/blocks`] as const; +}; + +export const getGetV1ListAvailableBlocksQueryOptions = < + TData = Awaited>, + TError = unknown, +>(options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; +}) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetV1ListAvailableBlocksQueryKey(); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => getV1ListAvailableBlocks({ signal, ...requestOptions }); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV1ListAvailableBlocksQueryResult = NonNullable< + Awaited> +>; +export type GetV1ListAvailableBlocksQueryError = unknown; + +export function useGetV1ListAvailableBlocks< + TData = Awaited>, + TError = unknown, +>( + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1ListAvailableBlocks< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1ListAvailableBlocks< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary List available blocks + */ + +export function useGetV1ListAvailableBlocks< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV1ListAvailableBlocksQueryOptions(options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Execute graph block + */ +export type postV1ExecuteGraphBlockResponse200 = { + data: PostV1ExecuteGraphBlock200; + status: 200; +}; + +export type postV1ExecuteGraphBlockResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV1ExecuteGraphBlockResponseComposite = + | postV1ExecuteGraphBlockResponse200 + | postV1ExecuteGraphBlockResponse422; + +export type postV1ExecuteGraphBlockResponse = + postV1ExecuteGraphBlockResponseComposite & { + headers: Headers; + }; + +export const getPostV1ExecuteGraphBlockUrl = (blockId: string) => { + return `/api/blocks/${blockId}/execute`; +}; + +export const postV1ExecuteGraphBlock = async ( + blockId: string, + postV1ExecuteGraphBlockBody: PostV1ExecuteGraphBlockBody, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV1ExecuteGraphBlockUrl(blockId), + { + ...options, + method: "POST", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(postV1ExecuteGraphBlockBody), + }, + ); +}; + +export const getPostV1ExecuteGraphBlockMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { blockId: string; data: PostV1ExecuteGraphBlockBody }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { blockId: string; data: PostV1ExecuteGraphBlockBody }, + TContext +> => { + const mutationKey = ["postV1ExecuteGraphBlock"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { blockId: string; data: PostV1ExecuteGraphBlockBody } + > = (props) => { + const { blockId, data } = props ?? {}; + + return postV1ExecuteGraphBlock(blockId, data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV1ExecuteGraphBlockMutationResult = NonNullable< + Awaited> +>; +export type PostV1ExecuteGraphBlockMutationBody = PostV1ExecuteGraphBlockBody; +export type PostV1ExecuteGraphBlockMutationError = HTTPValidationError; + +/** + * @summary Execute graph block + */ +export const usePostV1ExecuteGraphBlock = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { blockId: string; data: PostV1ExecuteGraphBlockBody }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { blockId: string; data: PostV1ExecuteGraphBlockBody }, + TContext +> => { + const mutationOptions = getPostV1ExecuteGraphBlockMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/endpoints/credits/credits.ts b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/credits/credits.ts new file mode 100644 index 0000000000..a6007de0f4 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/credits/credits.ts @@ -0,0 +1,1480 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import { useMutation, useQuery } from "@tanstack/react-query"; +import type { + DataTag, + DefinedInitialDataOptions, + DefinedUseQueryResult, + MutationFunction, + QueryClient, + QueryFunction, + QueryKey, + UndefinedInitialDataOptions, + UseMutationOptions, + UseMutationResult, + UseQueryOptions, + UseQueryResult, +} from "@tanstack/react-query"; + +import type { AutoTopUpConfig } from "../../models/autoTopUpConfig"; + +import type { GetV1GetCreditHistoryParams } from "../../models/getV1GetCreditHistoryParams"; + +import type { GetV1GetUserCredits200 } from "../../models/getV1GetUserCredits200"; + +import type { GetV1ManagePaymentMethods200 } from "../../models/getV1ManagePaymentMethods200"; + +import type { HTTPValidationError } from "../../models/hTTPValidationError"; + +import type { PostV1RefundCreditTransactionBody } from "../../models/postV1RefundCreditTransactionBody"; + +import type { RefundRequest } from "../../models/refundRequest"; + +import type { RequestTopUp } from "../../models/requestTopUp"; + +import type { TransactionHistory } from "../../models/transactionHistory"; + +import { customMutator } from "../../../mutators/custom-mutator"; + +type SecondParameter unknown> = Parameters[1]; + +/** + * @summary Get user credits + */ +export type getV1GetUserCreditsResponse200 = { + data: GetV1GetUserCredits200; + status: 200; +}; + +export type getV1GetUserCreditsResponseComposite = + getV1GetUserCreditsResponse200; + +export type getV1GetUserCreditsResponse = + getV1GetUserCreditsResponseComposite & { + headers: Headers; + }; + +export const getGetV1GetUserCreditsUrl = () => { + return `/api/credits`; +}; + +export const getV1GetUserCredits = async ( + options?: RequestInit, +): Promise => { + return customMutator( + getGetV1GetUserCreditsUrl(), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV1GetUserCreditsQueryKey = () => { + return [`/api/credits`] as const; +}; + +export const getGetV1GetUserCreditsQueryOptions = < + TData = Awaited>, + TError = unknown, +>(options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; +}) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getGetV1GetUserCreditsQueryKey(); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => getV1GetUserCredits({ signal, ...requestOptions }); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV1GetUserCreditsQueryResult = NonNullable< + Awaited> +>; +export type GetV1GetUserCreditsQueryError = unknown; + +export function useGetV1GetUserCredits< + TData = Awaited>, + TError = unknown, +>( + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetUserCredits< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetUserCredits< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Get user credits + */ + +export function useGetV1GetUserCredits< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV1GetUserCreditsQueryOptions(options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Request credit top up + */ +export type postV1RequestCreditTopUpResponse200 = { + data: unknown; + status: 200; +}; + +export type postV1RequestCreditTopUpResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV1RequestCreditTopUpResponseComposite = + | postV1RequestCreditTopUpResponse200 + | postV1RequestCreditTopUpResponse422; + +export type postV1RequestCreditTopUpResponse = + postV1RequestCreditTopUpResponseComposite & { + headers: Headers; + }; + +export const getPostV1RequestCreditTopUpUrl = () => { + return `/api/credits`; +}; + +export const postV1RequestCreditTopUp = async ( + requestTopUp: RequestTopUp, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV1RequestCreditTopUpUrl(), + { + ...options, + method: "POST", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(requestTopUp), + }, + ); +}; + +export const getPostV1RequestCreditTopUpMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: RequestTopUp }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { data: RequestTopUp }, + TContext +> => { + const mutationKey = ["postV1RequestCreditTopUp"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { data: RequestTopUp } + > = (props) => { + const { data } = props ?? {}; + + return postV1RequestCreditTopUp(data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV1RequestCreditTopUpMutationResult = NonNullable< + Awaited> +>; +export type PostV1RequestCreditTopUpMutationBody = RequestTopUp; +export type PostV1RequestCreditTopUpMutationError = HTTPValidationError; + +/** + * @summary Request credit top up + */ +export const usePostV1RequestCreditTopUp = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: RequestTopUp }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { data: RequestTopUp }, + TContext +> => { + const mutationOptions = getPostV1RequestCreditTopUpMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * @summary Fulfill checkout session + */ +export type patchV1FulfillCheckoutSessionResponse200 = { + data: unknown; + status: 200; +}; + +export type patchV1FulfillCheckoutSessionResponseComposite = + patchV1FulfillCheckoutSessionResponse200; + +export type patchV1FulfillCheckoutSessionResponse = + patchV1FulfillCheckoutSessionResponseComposite & { + headers: Headers; + }; + +export const getPatchV1FulfillCheckoutSessionUrl = () => { + return `/api/credits`; +}; + +export const patchV1FulfillCheckoutSession = async ( + options?: RequestInit, +): Promise => { + return customMutator( + getPatchV1FulfillCheckoutSessionUrl(), + { + ...options, + method: "PATCH", + }, + ); +}; + +export const getPatchV1FulfillCheckoutSessionMutationOptions = < + TError = unknown, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + void, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + void, + TContext +> => { + const mutationKey = ["patchV1FulfillCheckoutSession"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + void + > = () => { + return patchV1FulfillCheckoutSession(requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PatchV1FulfillCheckoutSessionMutationResult = NonNullable< + Awaited> +>; + +export type PatchV1FulfillCheckoutSessionMutationError = unknown; + +/** + * @summary Fulfill checkout session + */ +export const usePatchV1FulfillCheckoutSession = < + TError = unknown, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + void, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + void, + TContext +> => { + const mutationOptions = + getPatchV1FulfillCheckoutSessionMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * @summary Refund credit transaction + */ +export type postV1RefundCreditTransactionResponse200 = { + data: number; + status: 200; +}; + +export type postV1RefundCreditTransactionResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV1RefundCreditTransactionResponseComposite = + | postV1RefundCreditTransactionResponse200 + | postV1RefundCreditTransactionResponse422; + +export type postV1RefundCreditTransactionResponse = + postV1RefundCreditTransactionResponseComposite & { + headers: Headers; + }; + +export const getPostV1RefundCreditTransactionUrl = (transactionKey: string) => { + return `/api/credits/${transactionKey}/refund`; +}; + +export const postV1RefundCreditTransaction = async ( + transactionKey: string, + postV1RefundCreditTransactionBody: PostV1RefundCreditTransactionBody, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV1RefundCreditTransactionUrl(transactionKey), + { + ...options, + method: "POST", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(postV1RefundCreditTransactionBody), + }, + ); +}; + +export const getPostV1RefundCreditTransactionMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { transactionKey: string; data: PostV1RefundCreditTransactionBody }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { transactionKey: string; data: PostV1RefundCreditTransactionBody }, + TContext +> => { + const mutationKey = ["postV1RefundCreditTransaction"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { transactionKey: string; data: PostV1RefundCreditTransactionBody } + > = (props) => { + const { transactionKey, data } = props ?? {}; + + return postV1RefundCreditTransaction(transactionKey, data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV1RefundCreditTransactionMutationResult = NonNullable< + Awaited> +>; +export type PostV1RefundCreditTransactionMutationBody = + PostV1RefundCreditTransactionBody; +export type PostV1RefundCreditTransactionMutationError = HTTPValidationError; + +/** + * @summary Refund credit transaction + */ +export const usePostV1RefundCreditTransaction = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { transactionKey: string; data: PostV1RefundCreditTransactionBody }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { transactionKey: string; data: PostV1RefundCreditTransactionBody }, + TContext +> => { + const mutationOptions = + getPostV1RefundCreditTransactionMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * @summary Get auto top up + */ +export type getV1GetAutoTopUpResponse200 = { + data: AutoTopUpConfig; + status: 200; +}; + +export type getV1GetAutoTopUpResponseComposite = getV1GetAutoTopUpResponse200; + +export type getV1GetAutoTopUpResponse = getV1GetAutoTopUpResponseComposite & { + headers: Headers; +}; + +export const getGetV1GetAutoTopUpUrl = () => { + return `/api/credits/auto-top-up`; +}; + +export const getV1GetAutoTopUp = async ( + options?: RequestInit, +): Promise => { + return customMutator(getGetV1GetAutoTopUpUrl(), { + ...options, + method: "GET", + }); +}; + +export const getGetV1GetAutoTopUpQueryKey = () => { + return [`/api/credits/auto-top-up`] as const; +}; + +export const getGetV1GetAutoTopUpQueryOptions = < + TData = Awaited>, + TError = unknown, +>(options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; +}) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getGetV1GetAutoTopUpQueryKey(); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => getV1GetAutoTopUp({ signal, ...requestOptions }); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV1GetAutoTopUpQueryResult = NonNullable< + Awaited> +>; +export type GetV1GetAutoTopUpQueryError = unknown; + +export function useGetV1GetAutoTopUp< + TData = Awaited>, + TError = unknown, +>( + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetAutoTopUp< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetAutoTopUp< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Get auto top up + */ + +export function useGetV1GetAutoTopUp< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV1GetAutoTopUpQueryOptions(options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Configure auto top up + */ +export type postV1ConfigureAutoTopUpResponse200 = { + data: string; + status: 200; +}; + +export type postV1ConfigureAutoTopUpResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV1ConfigureAutoTopUpResponseComposite = + | postV1ConfigureAutoTopUpResponse200 + | postV1ConfigureAutoTopUpResponse422; + +export type postV1ConfigureAutoTopUpResponse = + postV1ConfigureAutoTopUpResponseComposite & { + headers: Headers; + }; + +export const getPostV1ConfigureAutoTopUpUrl = () => { + return `/api/credits/auto-top-up`; +}; + +export const postV1ConfigureAutoTopUp = async ( + autoTopUpConfig: AutoTopUpConfig, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV1ConfigureAutoTopUpUrl(), + { + ...options, + method: "POST", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(autoTopUpConfig), + }, + ); +}; + +export const getPostV1ConfigureAutoTopUpMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: AutoTopUpConfig }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { data: AutoTopUpConfig }, + TContext +> => { + const mutationKey = ["postV1ConfigureAutoTopUp"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { data: AutoTopUpConfig } + > = (props) => { + const { data } = props ?? {}; + + return postV1ConfigureAutoTopUp(data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV1ConfigureAutoTopUpMutationResult = NonNullable< + Awaited> +>; +export type PostV1ConfigureAutoTopUpMutationBody = AutoTopUpConfig; +export type PostV1ConfigureAutoTopUpMutationError = HTTPValidationError; + +/** + * @summary Configure auto top up + */ +export const usePostV1ConfigureAutoTopUp = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: AutoTopUpConfig }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { data: AutoTopUpConfig }, + TContext +> => { + const mutationOptions = getPostV1ConfigureAutoTopUpMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * @summary Handle Stripe webhooks + */ +export type postV1HandleStripeWebhooksResponse200 = { + data: unknown; + status: 200; +}; + +export type postV1HandleStripeWebhooksResponseComposite = + postV1HandleStripeWebhooksResponse200; + +export type postV1HandleStripeWebhooksResponse = + postV1HandleStripeWebhooksResponseComposite & { + headers: Headers; + }; + +export const getPostV1HandleStripeWebhooksUrl = () => { + return `/api/credits/stripe_webhook`; +}; + +export const postV1HandleStripeWebhooks = async ( + options?: RequestInit, +): Promise => { + return customMutator( + getPostV1HandleStripeWebhooksUrl(), + { + ...options, + method: "POST", + }, + ); +}; + +export const getPostV1HandleStripeWebhooksMutationOptions = < + TError = unknown, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + void, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + void, + TContext +> => { + const mutationKey = ["postV1HandleStripeWebhooks"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + void + > = () => { + return postV1HandleStripeWebhooks(requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV1HandleStripeWebhooksMutationResult = NonNullable< + Awaited> +>; + +export type PostV1HandleStripeWebhooksMutationError = unknown; + +/** + * @summary Handle Stripe webhooks + */ +export const usePostV1HandleStripeWebhooks = < + TError = unknown, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + void, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + void, + TContext +> => { + const mutationOptions = getPostV1HandleStripeWebhooksMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * @summary Manage payment methods + */ +export type getV1ManagePaymentMethodsResponse200 = { + data: GetV1ManagePaymentMethods200; + status: 200; +}; + +export type getV1ManagePaymentMethodsResponseComposite = + getV1ManagePaymentMethodsResponse200; + +export type getV1ManagePaymentMethodsResponse = + getV1ManagePaymentMethodsResponseComposite & { + headers: Headers; + }; + +export const getGetV1ManagePaymentMethodsUrl = () => { + return `/api/credits/manage`; +}; + +export const getV1ManagePaymentMethods = async ( + options?: RequestInit, +): Promise => { + return customMutator( + getGetV1ManagePaymentMethodsUrl(), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV1ManagePaymentMethodsQueryKey = () => { + return [`/api/credits/manage`] as const; +}; + +export const getGetV1ManagePaymentMethodsQueryOptions = < + TData = Awaited>, + TError = unknown, +>(options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; +}) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetV1ManagePaymentMethodsQueryKey(); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => getV1ManagePaymentMethods({ signal, ...requestOptions }); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV1ManagePaymentMethodsQueryResult = NonNullable< + Awaited> +>; +export type GetV1ManagePaymentMethodsQueryError = unknown; + +export function useGetV1ManagePaymentMethods< + TData = Awaited>, + TError = unknown, +>( + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1ManagePaymentMethods< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1ManagePaymentMethods< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Manage payment methods + */ + +export function useGetV1ManagePaymentMethods< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV1ManagePaymentMethodsQueryOptions(options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Get credit history + */ +export type getV1GetCreditHistoryResponse200 = { + data: TransactionHistory; + status: 200; +}; + +export type getV1GetCreditHistoryResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type getV1GetCreditHistoryResponseComposite = + | getV1GetCreditHistoryResponse200 + | getV1GetCreditHistoryResponse422; + +export type getV1GetCreditHistoryResponse = + getV1GetCreditHistoryResponseComposite & { + headers: Headers; + }; + +export const getGetV1GetCreditHistoryUrl = ( + params?: GetV1GetCreditHistoryParams, +) => { + const normalizedParams = new URLSearchParams(); + + Object.entries(params || {}).forEach(([key, value]) => { + if (value !== undefined) { + normalizedParams.append(key, value === null ? "null" : value.toString()); + } + }); + + const stringifiedParams = normalizedParams.toString(); + + return stringifiedParams.length > 0 + ? `/api/credits/transactions?${stringifiedParams}` + : `/api/credits/transactions`; +}; + +export const getV1GetCreditHistory = async ( + params?: GetV1GetCreditHistoryParams, + options?: RequestInit, +): Promise => { + return customMutator( + getGetV1GetCreditHistoryUrl(params), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV1GetCreditHistoryQueryKey = ( + params?: GetV1GetCreditHistoryParams, +) => { + return [`/api/credits/transactions`, ...(params ? [params] : [])] as const; +}; + +export const getGetV1GetCreditHistoryQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: GetV1GetCreditHistoryParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetV1GetCreditHistoryQueryKey(params); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV1GetCreditHistory(params, { signal, ...requestOptions }); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV1GetCreditHistoryQueryResult = NonNullable< + Awaited> +>; +export type GetV1GetCreditHistoryQueryError = HTTPValidationError; + +export function useGetV1GetCreditHistory< + TData = Awaited>, + TError = HTTPValidationError, +>( + params: undefined | GetV1GetCreditHistoryParams, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetCreditHistory< + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: GetV1GetCreditHistoryParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetCreditHistory< + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: GetV1GetCreditHistoryParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Get credit history + */ + +export function useGetV1GetCreditHistory< + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: GetV1GetCreditHistoryParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV1GetCreditHistoryQueryOptions(params, options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Get refund requests + */ +export type getV1GetRefundRequestsResponse200 = { + data: RefundRequest[]; + status: 200; +}; + +export type getV1GetRefundRequestsResponseComposite = + getV1GetRefundRequestsResponse200; + +export type getV1GetRefundRequestsResponse = + getV1GetRefundRequestsResponseComposite & { + headers: Headers; + }; + +export const getGetV1GetRefundRequestsUrl = () => { + return `/api/credits/refunds`; +}; + +export const getV1GetRefundRequests = async ( + options?: RequestInit, +): Promise => { + return customMutator( + getGetV1GetRefundRequestsUrl(), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV1GetRefundRequestsQueryKey = () => { + return [`/api/credits/refunds`] as const; +}; + +export const getGetV1GetRefundRequestsQueryOptions = < + TData = Awaited>, + TError = unknown, +>(options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; +}) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetV1GetRefundRequestsQueryKey(); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => getV1GetRefundRequests({ signal, ...requestOptions }); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV1GetRefundRequestsQueryResult = NonNullable< + Awaited> +>; +export type GetV1GetRefundRequestsQueryError = unknown; + +export function useGetV1GetRefundRequests< + TData = Awaited>, + TError = unknown, +>( + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetRefundRequests< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetRefundRequests< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Get refund requests + */ + +export function useGetV1GetRefundRequests< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV1GetRefundRequestsQueryOptions(options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/endpoints/email/email.ts b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/email/email.ts new file mode 100644 index 0000000000..fd0d22ee0b --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/email/email.ts @@ -0,0 +1,265 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import { useMutation } from "@tanstack/react-query"; +import type { + MutationFunction, + QueryClient, + UseMutationOptions, + UseMutationResult, +} from "@tanstack/react-query"; + +import type { HTTPValidationError } from "../../models/hTTPValidationError"; + +import type { PostV1HandlePostmarkEmailWebhooksBody } from "../../models/postV1HandlePostmarkEmailWebhooksBody"; + +import type { PostV1OneClickEmailUnsubscribeParams } from "../../models/postV1OneClickEmailUnsubscribeParams"; + +import { customMutator } from "../../../mutators/custom-mutator"; + +type SecondParameter unknown> = Parameters[1]; + +/** + * @summary One Click Email Unsubscribe + */ +export type postV1OneClickEmailUnsubscribeResponse200 = { + data: unknown; + status: 200; +}; + +export type postV1OneClickEmailUnsubscribeResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV1OneClickEmailUnsubscribeResponseComposite = + | postV1OneClickEmailUnsubscribeResponse200 + | postV1OneClickEmailUnsubscribeResponse422; + +export type postV1OneClickEmailUnsubscribeResponse = + postV1OneClickEmailUnsubscribeResponseComposite & { + headers: Headers; + }; + +export const getPostV1OneClickEmailUnsubscribeUrl = ( + params: PostV1OneClickEmailUnsubscribeParams, +) => { + const normalizedParams = new URLSearchParams(); + + Object.entries(params || {}).forEach(([key, value]) => { + if (value !== undefined) { + normalizedParams.append(key, value === null ? "null" : value.toString()); + } + }); + + const stringifiedParams = normalizedParams.toString(); + + return stringifiedParams.length > 0 + ? `/api/email/unsubscribe?${stringifiedParams}` + : `/api/email/unsubscribe`; +}; + +export const postV1OneClickEmailUnsubscribe = async ( + params: PostV1OneClickEmailUnsubscribeParams, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV1OneClickEmailUnsubscribeUrl(params), + { + ...options, + method: "POST", + }, + ); +}; + +export const getPostV1OneClickEmailUnsubscribeMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { params: PostV1OneClickEmailUnsubscribeParams }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { params: PostV1OneClickEmailUnsubscribeParams }, + TContext +> => { + const mutationKey = ["postV1OneClickEmailUnsubscribe"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { params: PostV1OneClickEmailUnsubscribeParams } + > = (props) => { + const { params } = props ?? {}; + + return postV1OneClickEmailUnsubscribe(params, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV1OneClickEmailUnsubscribeMutationResult = NonNullable< + Awaited> +>; + +export type PostV1OneClickEmailUnsubscribeMutationError = HTTPValidationError; + +/** + * @summary One Click Email Unsubscribe + */ +export const usePostV1OneClickEmailUnsubscribe = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { params: PostV1OneClickEmailUnsubscribeParams }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { params: PostV1OneClickEmailUnsubscribeParams }, + TContext +> => { + const mutationOptions = + getPostV1OneClickEmailUnsubscribeMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * @summary Handle Postmark Email Webhooks + */ +export type postV1HandlePostmarkEmailWebhooksResponse200 = { + data: unknown; + status: 200; +}; + +export type postV1HandlePostmarkEmailWebhooksResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV1HandlePostmarkEmailWebhooksResponseComposite = + | postV1HandlePostmarkEmailWebhooksResponse200 + | postV1HandlePostmarkEmailWebhooksResponse422; + +export type postV1HandlePostmarkEmailWebhooksResponse = + postV1HandlePostmarkEmailWebhooksResponseComposite & { + headers: Headers; + }; + +export const getPostV1HandlePostmarkEmailWebhooksUrl = () => { + return `/api/email/`; +}; + +export const postV1HandlePostmarkEmailWebhooks = async ( + postV1HandlePostmarkEmailWebhooksBody: PostV1HandlePostmarkEmailWebhooksBody, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV1HandlePostmarkEmailWebhooksUrl(), + { + ...options, + method: "POST", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(postV1HandlePostmarkEmailWebhooksBody), + }, + ); +}; + +export const getPostV1HandlePostmarkEmailWebhooksMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: PostV1HandlePostmarkEmailWebhooksBody }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { data: PostV1HandlePostmarkEmailWebhooksBody }, + TContext +> => { + const mutationKey = ["postV1HandlePostmarkEmailWebhooks"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { data: PostV1HandlePostmarkEmailWebhooksBody } + > = (props) => { + const { data } = props ?? {}; + + return postV1HandlePostmarkEmailWebhooks(data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV1HandlePostmarkEmailWebhooksMutationResult = NonNullable< + Awaited> +>; +export type PostV1HandlePostmarkEmailWebhooksMutationBody = + PostV1HandlePostmarkEmailWebhooksBody; +export type PostV1HandlePostmarkEmailWebhooksMutationError = + HTTPValidationError; + +/** + * @summary Handle Postmark Email Webhooks + */ +export const usePostV1HandlePostmarkEmailWebhooks = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: PostV1HandlePostmarkEmailWebhooksBody }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { data: PostV1HandlePostmarkEmailWebhooksBody }, + TContext +> => { + const mutationOptions = + getPostV1HandlePostmarkEmailWebhooksMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/endpoints/graphs/graphs.ts b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/graphs/graphs.ts new file mode 100644 index 0000000000..4f249e2548 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/graphs/graphs.ts @@ -0,0 +1,2302 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import { useMutation, useQuery } from "@tanstack/react-query"; +import type { + DataTag, + DefinedInitialDataOptions, + DefinedUseQueryResult, + MutationFunction, + QueryClient, + QueryFunction, + QueryKey, + UndefinedInitialDataOptions, + UseMutationOptions, + UseMutationResult, + UseQueryOptions, + UseQueryResult, +} from "@tanstack/react-query"; + +import type { BodyPostV1ExecuteGraphAgent } from "../../models/bodyPostV1ExecuteGraphAgent"; + +import type { CreateGraph } from "../../models/createGraph"; + +import type { DeleteGraphResponse } from "../../models/deleteGraphResponse"; + +import type { ExecuteGraphResponse } from "../../models/executeGraphResponse"; + +import type { GetV1GetExecutionDetails200 } from "../../models/getV1GetExecutionDetails200"; + +import type { GetV1GetGraphVersionParams } from "../../models/getV1GetGraphVersionParams"; + +import type { GetV1GetSpecificGraphParams } from "../../models/getV1GetSpecificGraphParams"; + +import type { Graph } from "../../models/graph"; + +import type { GraphExecution } from "../../models/graphExecution"; + +import type { GraphExecutionMeta } from "../../models/graphExecutionMeta"; + +import type { GraphModel } from "../../models/graphModel"; + +import type { HTTPValidationError } from "../../models/hTTPValidationError"; + +import type { PostV1ExecuteGraphAgentParams } from "../../models/postV1ExecuteGraphAgentParams"; + +import type { SetGraphActiveVersion } from "../../models/setGraphActiveVersion"; + +import { customMutator } from "../../../mutators/custom-mutator"; + +type SecondParameter unknown> = Parameters[1]; + +/** + * @summary List user graphs + */ +export type getV1ListUserGraphsResponse200 = { + data: GraphModel[]; + status: 200; +}; + +export type getV1ListUserGraphsResponseComposite = + getV1ListUserGraphsResponse200; + +export type getV1ListUserGraphsResponse = + getV1ListUserGraphsResponseComposite & { + headers: Headers; + }; + +export const getGetV1ListUserGraphsUrl = () => { + return `/api/graphs`; +}; + +export const getV1ListUserGraphs = async ( + options?: RequestInit, +): Promise => { + return customMutator( + getGetV1ListUserGraphsUrl(), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV1ListUserGraphsQueryKey = () => { + return [`/api/graphs`] as const; +}; + +export const getGetV1ListUserGraphsQueryOptions = < + TData = Awaited>, + TError = unknown, +>(options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; +}) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getGetV1ListUserGraphsQueryKey(); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => getV1ListUserGraphs({ signal, ...requestOptions }); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV1ListUserGraphsQueryResult = NonNullable< + Awaited> +>; +export type GetV1ListUserGraphsQueryError = unknown; + +export function useGetV1ListUserGraphs< + TData = Awaited>, + TError = unknown, +>( + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1ListUserGraphs< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1ListUserGraphs< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary List user graphs + */ + +export function useGetV1ListUserGraphs< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV1ListUserGraphsQueryOptions(options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Create new graph + */ +export type postV1CreateNewGraphResponse200 = { + data: GraphModel; + status: 200; +}; + +export type postV1CreateNewGraphResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV1CreateNewGraphResponseComposite = + | postV1CreateNewGraphResponse200 + | postV1CreateNewGraphResponse422; + +export type postV1CreateNewGraphResponse = + postV1CreateNewGraphResponseComposite & { + headers: Headers; + }; + +export const getPostV1CreateNewGraphUrl = () => { + return `/api/graphs`; +}; + +export const postV1CreateNewGraph = async ( + createGraph: CreateGraph, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV1CreateNewGraphUrl(), + { + ...options, + method: "POST", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(createGraph), + }, + ); +}; + +export const getPostV1CreateNewGraphMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: CreateGraph }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { data: CreateGraph }, + TContext +> => { + const mutationKey = ["postV1CreateNewGraph"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { data: CreateGraph } + > = (props) => { + const { data } = props ?? {}; + + return postV1CreateNewGraph(data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV1CreateNewGraphMutationResult = NonNullable< + Awaited> +>; +export type PostV1CreateNewGraphMutationBody = CreateGraph; +export type PostV1CreateNewGraphMutationError = HTTPValidationError; + +/** + * @summary Create new graph + */ +export const usePostV1CreateNewGraph = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: CreateGraph }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { data: CreateGraph }, + TContext +> => { + const mutationOptions = getPostV1CreateNewGraphMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * @summary Get graph version + */ +export type getV1GetGraphVersionResponse200 = { + data: GraphModel; + status: 200; +}; + +export type getV1GetGraphVersionResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type getV1GetGraphVersionResponseComposite = + | getV1GetGraphVersionResponse200 + | getV1GetGraphVersionResponse422; + +export type getV1GetGraphVersionResponse = + getV1GetGraphVersionResponseComposite & { + headers: Headers; + }; + +export const getGetV1GetGraphVersionUrl = ( + graphId: string, + version: number | null, + params?: GetV1GetGraphVersionParams, +) => { + const normalizedParams = new URLSearchParams(); + + Object.entries(params || {}).forEach(([key, value]) => { + if (value !== undefined) { + normalizedParams.append(key, value === null ? "null" : value.toString()); + } + }); + + const stringifiedParams = normalizedParams.toString(); + + return stringifiedParams.length > 0 + ? `/api/graphs/${graphId}/versions/${version}?${stringifiedParams}` + : `/api/graphs/${graphId}/versions/${version}`; +}; + +export const getV1GetGraphVersion = async ( + graphId: string, + version: number | null, + params?: GetV1GetGraphVersionParams, + options?: RequestInit, +): Promise => { + return customMutator( + getGetV1GetGraphVersionUrl(graphId, version, params), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV1GetGraphVersionQueryKey = ( + graphId: string, + version: number | null, + params?: GetV1GetGraphVersionParams, +) => { + return [ + `/api/graphs/${graphId}/versions/${version}`, + ...(params ? [params] : []), + ] as const; +}; + +export const getGetV1GetGraphVersionQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + version: number | null, + params?: GetV1GetGraphVersionParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? + getGetV1GetGraphVersionQueryKey(graphId, version, params); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV1GetGraphVersion(graphId, version, params, { + signal, + ...requestOptions, + }); + + return { + queryKey, + queryFn, + enabled: !!(graphId && version), + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV1GetGraphVersionQueryResult = NonNullable< + Awaited> +>; +export type GetV1GetGraphVersionQueryError = HTTPValidationError; + +export function useGetV1GetGraphVersion< + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + version: number | null, + params: undefined | GetV1GetGraphVersionParams, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetGraphVersion< + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + version: number | null, + params?: GetV1GetGraphVersionParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetGraphVersion< + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + version: number | null, + params?: GetV1GetGraphVersionParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Get graph version + */ + +export function useGetV1GetGraphVersion< + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + version: number | null, + params?: GetV1GetGraphVersionParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV1GetGraphVersionQueryOptions( + graphId, + version, + params, + options, + ); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Get specific graph + */ +export type getV1GetSpecificGraphResponse200 = { + data: GraphModel; + status: 200; +}; + +export type getV1GetSpecificGraphResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type getV1GetSpecificGraphResponseComposite = + | getV1GetSpecificGraphResponse200 + | getV1GetSpecificGraphResponse422; + +export type getV1GetSpecificGraphResponse = + getV1GetSpecificGraphResponseComposite & { + headers: Headers; + }; + +export const getGetV1GetSpecificGraphUrl = ( + graphId: string, + params?: GetV1GetSpecificGraphParams, +) => { + const normalizedParams = new URLSearchParams(); + + Object.entries(params || {}).forEach(([key, value]) => { + if (value !== undefined) { + normalizedParams.append(key, value === null ? "null" : value.toString()); + } + }); + + const stringifiedParams = normalizedParams.toString(); + + return stringifiedParams.length > 0 + ? `/api/graphs/${graphId}?${stringifiedParams}` + : `/api/graphs/${graphId}`; +}; + +export const getV1GetSpecificGraph = async ( + graphId: string, + params?: GetV1GetSpecificGraphParams, + options?: RequestInit, +): Promise => { + return customMutator( + getGetV1GetSpecificGraphUrl(graphId, params), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV1GetSpecificGraphQueryKey = ( + graphId: string, + params?: GetV1GetSpecificGraphParams, +) => { + return [`/api/graphs/${graphId}`, ...(params ? [params] : [])] as const; +}; + +export const getGetV1GetSpecificGraphQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + params?: GetV1GetSpecificGraphParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetV1GetSpecificGraphQueryKey(graphId, params); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV1GetSpecificGraph(graphId, params, { signal, ...requestOptions }); + + return { + queryKey, + queryFn, + enabled: !!graphId, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV1GetSpecificGraphQueryResult = NonNullable< + Awaited> +>; +export type GetV1GetSpecificGraphQueryError = HTTPValidationError; + +export function useGetV1GetSpecificGraph< + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + params: undefined | GetV1GetSpecificGraphParams, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetSpecificGraph< + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + params?: GetV1GetSpecificGraphParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetSpecificGraph< + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + params?: GetV1GetSpecificGraphParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Get specific graph + */ + +export function useGetV1GetSpecificGraph< + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + params?: GetV1GetSpecificGraphParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV1GetSpecificGraphQueryOptions( + graphId, + params, + options, + ); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Delete graph permanently + */ +export type deleteV1DeleteGraphPermanentlyResponse200 = { + data: DeleteGraphResponse; + status: 200; +}; + +export type deleteV1DeleteGraphPermanentlyResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type deleteV1DeleteGraphPermanentlyResponseComposite = + | deleteV1DeleteGraphPermanentlyResponse200 + | deleteV1DeleteGraphPermanentlyResponse422; + +export type deleteV1DeleteGraphPermanentlyResponse = + deleteV1DeleteGraphPermanentlyResponseComposite & { + headers: Headers; + }; + +export const getDeleteV1DeleteGraphPermanentlyUrl = (graphId: string) => { + return `/api/graphs/${graphId}`; +}; + +export const deleteV1DeleteGraphPermanently = async ( + graphId: string, + options?: RequestInit, +): Promise => { + return customMutator( + getDeleteV1DeleteGraphPermanentlyUrl(graphId), + { + ...options, + method: "DELETE", + }, + ); +}; + +export const getDeleteV1DeleteGraphPermanentlyMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { graphId: string }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { graphId: string }, + TContext +> => { + const mutationKey = ["deleteV1DeleteGraphPermanently"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { graphId: string } + > = (props) => { + const { graphId } = props ?? {}; + + return deleteV1DeleteGraphPermanently(graphId, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type DeleteV1DeleteGraphPermanentlyMutationResult = NonNullable< + Awaited> +>; + +export type DeleteV1DeleteGraphPermanentlyMutationError = HTTPValidationError; + +/** + * @summary Delete graph permanently + */ +export const useDeleteV1DeleteGraphPermanently = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { graphId: string }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { graphId: string }, + TContext +> => { + const mutationOptions = + getDeleteV1DeleteGraphPermanentlyMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * @summary Update graph version + */ +export type putV1UpdateGraphVersionResponse200 = { + data: GraphModel; + status: 200; +}; + +export type putV1UpdateGraphVersionResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type putV1UpdateGraphVersionResponseComposite = + | putV1UpdateGraphVersionResponse200 + | putV1UpdateGraphVersionResponse422; + +export type putV1UpdateGraphVersionResponse = + putV1UpdateGraphVersionResponseComposite & { + headers: Headers; + }; + +export const getPutV1UpdateGraphVersionUrl = (graphId: string) => { + return `/api/graphs/${graphId}`; +}; + +export const putV1UpdateGraphVersion = async ( + graphId: string, + graph: Graph, + options?: RequestInit, +): Promise => { + return customMutator( + getPutV1UpdateGraphVersionUrl(graphId), + { + ...options, + method: "PUT", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(graph), + }, + ); +}; + +export const getPutV1UpdateGraphVersionMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { graphId: string; data: Graph }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { graphId: string; data: Graph }, + TContext +> => { + const mutationKey = ["putV1UpdateGraphVersion"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { graphId: string; data: Graph } + > = (props) => { + const { graphId, data } = props ?? {}; + + return putV1UpdateGraphVersion(graphId, data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PutV1UpdateGraphVersionMutationResult = NonNullable< + Awaited> +>; +export type PutV1UpdateGraphVersionMutationBody = Graph; +export type PutV1UpdateGraphVersionMutationError = HTTPValidationError; + +/** + * @summary Update graph version + */ +export const usePutV1UpdateGraphVersion = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { graphId: string; data: Graph }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { graphId: string; data: Graph }, + TContext +> => { + const mutationOptions = getPutV1UpdateGraphVersionMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * @summary Get all graph versions + */ +export type getV1GetAllGraphVersionsResponse200 = { + data: GraphModel[]; + status: 200; +}; + +export type getV1GetAllGraphVersionsResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type getV1GetAllGraphVersionsResponseComposite = + | getV1GetAllGraphVersionsResponse200 + | getV1GetAllGraphVersionsResponse422; + +export type getV1GetAllGraphVersionsResponse = + getV1GetAllGraphVersionsResponseComposite & { + headers: Headers; + }; + +export const getGetV1GetAllGraphVersionsUrl = (graphId: string) => { + return `/api/graphs/${graphId}/versions`; +}; + +export const getV1GetAllGraphVersions = async ( + graphId: string, + options?: RequestInit, +): Promise => { + return customMutator( + getGetV1GetAllGraphVersionsUrl(graphId), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV1GetAllGraphVersionsQueryKey = (graphId: string) => { + return [`/api/graphs/${graphId}/versions`] as const; +}; + +export const getGetV1GetAllGraphVersionsQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetV1GetAllGraphVersionsQueryKey(graphId); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV1GetAllGraphVersions(graphId, { signal, ...requestOptions }); + + return { + queryKey, + queryFn, + enabled: !!graphId, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV1GetAllGraphVersionsQueryResult = NonNullable< + Awaited> +>; +export type GetV1GetAllGraphVersionsQueryError = HTTPValidationError; + +export function useGetV1GetAllGraphVersions< + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetAllGraphVersions< + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetAllGraphVersions< + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Get all graph versions + */ + +export function useGetV1GetAllGraphVersions< + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV1GetAllGraphVersionsQueryOptions( + graphId, + options, + ); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Set active graph version + */ +export type putV1SetActiveGraphVersionResponse200 = { + data: unknown; + status: 200; +}; + +export type putV1SetActiveGraphVersionResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type putV1SetActiveGraphVersionResponseComposite = + | putV1SetActiveGraphVersionResponse200 + | putV1SetActiveGraphVersionResponse422; + +export type putV1SetActiveGraphVersionResponse = + putV1SetActiveGraphVersionResponseComposite & { + headers: Headers; + }; + +export const getPutV1SetActiveGraphVersionUrl = (graphId: string) => { + return `/api/graphs/${graphId}/versions/active`; +}; + +export const putV1SetActiveGraphVersion = async ( + graphId: string, + setGraphActiveVersion: SetGraphActiveVersion, + options?: RequestInit, +): Promise => { + return customMutator( + getPutV1SetActiveGraphVersionUrl(graphId), + { + ...options, + method: "PUT", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(setGraphActiveVersion), + }, + ); +}; + +export const getPutV1SetActiveGraphVersionMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { graphId: string; data: SetGraphActiveVersion }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { graphId: string; data: SetGraphActiveVersion }, + TContext +> => { + const mutationKey = ["putV1SetActiveGraphVersion"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { graphId: string; data: SetGraphActiveVersion } + > = (props) => { + const { graphId, data } = props ?? {}; + + return putV1SetActiveGraphVersion(graphId, data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PutV1SetActiveGraphVersionMutationResult = NonNullable< + Awaited> +>; +export type PutV1SetActiveGraphVersionMutationBody = SetGraphActiveVersion; +export type PutV1SetActiveGraphVersionMutationError = HTTPValidationError; + +/** + * @summary Set active graph version + */ +export const usePutV1SetActiveGraphVersion = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { graphId: string; data: SetGraphActiveVersion }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { graphId: string; data: SetGraphActiveVersion }, + TContext +> => { + const mutationOptions = getPutV1SetActiveGraphVersionMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * @summary Execute graph agent + */ +export type postV1ExecuteGraphAgentResponse200 = { + data: ExecuteGraphResponse; + status: 200; +}; + +export type postV1ExecuteGraphAgentResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV1ExecuteGraphAgentResponseComposite = + | postV1ExecuteGraphAgentResponse200 + | postV1ExecuteGraphAgentResponse422; + +export type postV1ExecuteGraphAgentResponse = + postV1ExecuteGraphAgentResponseComposite & { + headers: Headers; + }; + +export const getPostV1ExecuteGraphAgentUrl = ( + graphId: string, + graphVersion: number | null, + params?: PostV1ExecuteGraphAgentParams, +) => { + const normalizedParams = new URLSearchParams(); + + Object.entries(params || {}).forEach(([key, value]) => { + if (value !== undefined) { + normalizedParams.append(key, value === null ? "null" : value.toString()); + } + }); + + const stringifiedParams = normalizedParams.toString(); + + return stringifiedParams.length > 0 + ? `/api/graphs/${graphId}/execute/${graphVersion}?${stringifiedParams}` + : `/api/graphs/${graphId}/execute/${graphVersion}`; +}; + +export const postV1ExecuteGraphAgent = async ( + graphId: string, + graphVersion: number | null, + bodyPostV1ExecuteGraphAgent: BodyPostV1ExecuteGraphAgent, + params?: PostV1ExecuteGraphAgentParams, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV1ExecuteGraphAgentUrl(graphId, graphVersion, params), + { + ...options, + method: "POST", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(bodyPostV1ExecuteGraphAgent), + }, + ); +}; + +export const getPostV1ExecuteGraphAgentMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { + graphId: string; + graphVersion: number | null; + data: BodyPostV1ExecuteGraphAgent; + params?: PostV1ExecuteGraphAgentParams; + }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { + graphId: string; + graphVersion: number | null; + data: BodyPostV1ExecuteGraphAgent; + params?: PostV1ExecuteGraphAgentParams; + }, + TContext +> => { + const mutationKey = ["postV1ExecuteGraphAgent"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { + graphId: string; + graphVersion: number | null; + data: BodyPostV1ExecuteGraphAgent; + params?: PostV1ExecuteGraphAgentParams; + } + > = (props) => { + const { graphId, graphVersion, data, params } = props ?? {}; + + return postV1ExecuteGraphAgent( + graphId, + graphVersion, + data, + params, + requestOptions, + ); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV1ExecuteGraphAgentMutationResult = NonNullable< + Awaited> +>; +export type PostV1ExecuteGraphAgentMutationBody = BodyPostV1ExecuteGraphAgent; +export type PostV1ExecuteGraphAgentMutationError = HTTPValidationError; + +/** + * @summary Execute graph agent + */ +export const usePostV1ExecuteGraphAgent = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { + graphId: string; + graphVersion: number | null; + data: BodyPostV1ExecuteGraphAgent; + params?: PostV1ExecuteGraphAgentParams; + }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { + graphId: string; + graphVersion: number | null; + data: BodyPostV1ExecuteGraphAgent; + params?: PostV1ExecuteGraphAgentParams; + }, + TContext +> => { + const mutationOptions = getPostV1ExecuteGraphAgentMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * @summary Stop graph execution + */ +export type postV1StopGraphExecutionResponse200 = { + data: GraphExecution; + status: 200; +}; + +export type postV1StopGraphExecutionResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV1StopGraphExecutionResponseComposite = + | postV1StopGraphExecutionResponse200 + | postV1StopGraphExecutionResponse422; + +export type postV1StopGraphExecutionResponse = + postV1StopGraphExecutionResponseComposite & { + headers: Headers; + }; + +export const getPostV1StopGraphExecutionUrl = ( + graphId: string, + graphExecId: string, +) => { + return `/api/graphs/${graphId}/executions/${graphExecId}/stop`; +}; + +export const postV1StopGraphExecution = async ( + graphId: string, + graphExecId: string, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV1StopGraphExecutionUrl(graphId, graphExecId), + { + ...options, + method: "POST", + }, + ); +}; + +export const getPostV1StopGraphExecutionMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { graphId: string; graphExecId: string }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { graphId: string; graphExecId: string }, + TContext +> => { + const mutationKey = ["postV1StopGraphExecution"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { graphId: string; graphExecId: string } + > = (props) => { + const { graphId, graphExecId } = props ?? {}; + + return postV1StopGraphExecution(graphId, graphExecId, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV1StopGraphExecutionMutationResult = NonNullable< + Awaited> +>; + +export type PostV1StopGraphExecutionMutationError = HTTPValidationError; + +/** + * @summary Stop graph execution + */ +export const usePostV1StopGraphExecution = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { graphId: string; graphExecId: string }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { graphId: string; graphExecId: string }, + TContext +> => { + const mutationOptions = getPostV1StopGraphExecutionMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * @summary Get all executions + */ +export type getV1GetAllExecutionsResponse200 = { + data: GraphExecutionMeta[]; + status: 200; +}; + +export type getV1GetAllExecutionsResponseComposite = + getV1GetAllExecutionsResponse200; + +export type getV1GetAllExecutionsResponse = + getV1GetAllExecutionsResponseComposite & { + headers: Headers; + }; + +export const getGetV1GetAllExecutionsUrl = () => { + return `/api/executions`; +}; + +export const getV1GetAllExecutions = async ( + options?: RequestInit, +): Promise => { + return customMutator( + getGetV1GetAllExecutionsUrl(), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV1GetAllExecutionsQueryKey = () => { + return [`/api/executions`] as const; +}; + +export const getGetV1GetAllExecutionsQueryOptions = < + TData = Awaited>, + TError = unknown, +>(options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; +}) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getGetV1GetAllExecutionsQueryKey(); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => getV1GetAllExecutions({ signal, ...requestOptions }); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV1GetAllExecutionsQueryResult = NonNullable< + Awaited> +>; +export type GetV1GetAllExecutionsQueryError = unknown; + +export function useGetV1GetAllExecutions< + TData = Awaited>, + TError = unknown, +>( + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetAllExecutions< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetAllExecutions< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Get all executions + */ + +export function useGetV1GetAllExecutions< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV1GetAllExecutionsQueryOptions(options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Get graph executions + */ +export type getV1GetGraphExecutionsResponse200 = { + data: GraphExecutionMeta[]; + status: 200; +}; + +export type getV1GetGraphExecutionsResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type getV1GetGraphExecutionsResponseComposite = + | getV1GetGraphExecutionsResponse200 + | getV1GetGraphExecutionsResponse422; + +export type getV1GetGraphExecutionsResponse = + getV1GetGraphExecutionsResponseComposite & { + headers: Headers; + }; + +export const getGetV1GetGraphExecutionsUrl = (graphId: string) => { + return `/api/graphs/${graphId}/executions`; +}; + +export const getV1GetGraphExecutions = async ( + graphId: string, + options?: RequestInit, +): Promise => { + return customMutator( + getGetV1GetGraphExecutionsUrl(graphId), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV1GetGraphExecutionsQueryKey = (graphId: string) => { + return [`/api/graphs/${graphId}/executions`] as const; +}; + +export const getGetV1GetGraphExecutionsQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetV1GetGraphExecutionsQueryKey(graphId); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV1GetGraphExecutions(graphId, { signal, ...requestOptions }); + + return { + queryKey, + queryFn, + enabled: !!graphId, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV1GetGraphExecutionsQueryResult = NonNullable< + Awaited> +>; +export type GetV1GetGraphExecutionsQueryError = HTTPValidationError; + +export function useGetV1GetGraphExecutions< + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetGraphExecutions< + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetGraphExecutions< + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Get graph executions + */ + +export function useGetV1GetGraphExecutions< + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV1GetGraphExecutionsQueryOptions(graphId, options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Get execution details + */ +export type getV1GetExecutionDetailsResponse200 = { + data: GetV1GetExecutionDetails200; + status: 200; +}; + +export type getV1GetExecutionDetailsResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type getV1GetExecutionDetailsResponseComposite = + | getV1GetExecutionDetailsResponse200 + | getV1GetExecutionDetailsResponse422; + +export type getV1GetExecutionDetailsResponse = + getV1GetExecutionDetailsResponseComposite & { + headers: Headers; + }; + +export const getGetV1GetExecutionDetailsUrl = ( + graphId: string, + graphExecId: string, +) => { + return `/api/graphs/${graphId}/executions/${graphExecId}`; +}; + +export const getV1GetExecutionDetails = async ( + graphId: string, + graphExecId: string, + options?: RequestInit, +): Promise => { + return customMutator( + getGetV1GetExecutionDetailsUrl(graphId, graphExecId), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV1GetExecutionDetailsQueryKey = ( + graphId: string, + graphExecId: string, +) => { + return [`/api/graphs/${graphId}/executions/${graphExecId}`] as const; +}; + +export const getGetV1GetExecutionDetailsQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + graphExecId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? + getGetV1GetExecutionDetailsQueryKey(graphId, graphExecId); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV1GetExecutionDetails(graphId, graphExecId, { + signal, + ...requestOptions, + }); + + return { + queryKey, + queryFn, + enabled: !!(graphId && graphExecId), + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV1GetExecutionDetailsQueryResult = NonNullable< + Awaited> +>; +export type GetV1GetExecutionDetailsQueryError = HTTPValidationError; + +export function useGetV1GetExecutionDetails< + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + graphExecId: string, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetExecutionDetails< + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + graphExecId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetExecutionDetails< + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + graphExecId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Get execution details + */ + +export function useGetV1GetExecutionDetails< + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + graphExecId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV1GetExecutionDetailsQueryOptions( + graphId, + graphExecId, + options, + ); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Delete graph execution + */ +export type deleteV1DeleteGraphExecutionResponse204 = { + data: void; + status: 204; +}; + +export type deleteV1DeleteGraphExecutionResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type deleteV1DeleteGraphExecutionResponseComposite = + | deleteV1DeleteGraphExecutionResponse204 + | deleteV1DeleteGraphExecutionResponse422; + +export type deleteV1DeleteGraphExecutionResponse = + deleteV1DeleteGraphExecutionResponseComposite & { + headers: Headers; + }; + +export const getDeleteV1DeleteGraphExecutionUrl = (graphExecId: string) => { + return `/api/executions/${graphExecId}`; +}; + +export const deleteV1DeleteGraphExecution = async ( + graphExecId: string, + options?: RequestInit, +): Promise => { + return customMutator( + getDeleteV1DeleteGraphExecutionUrl(graphExecId), + { + ...options, + method: "DELETE", + }, + ); +}; + +export const getDeleteV1DeleteGraphExecutionMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { graphExecId: string }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { graphExecId: string }, + TContext +> => { + const mutationKey = ["deleteV1DeleteGraphExecution"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { graphExecId: string } + > = (props) => { + const { graphExecId } = props ?? {}; + + return deleteV1DeleteGraphExecution(graphExecId, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type DeleteV1DeleteGraphExecutionMutationResult = NonNullable< + Awaited> +>; + +export type DeleteV1DeleteGraphExecutionMutationError = HTTPValidationError; + +/** + * @summary Delete graph execution + */ +export const useDeleteV1DeleteGraphExecution = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { graphExecId: string }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { graphExecId: string }, + TContext +> => { + const mutationOptions = + getDeleteV1DeleteGraphExecutionMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/endpoints/health/health.ts b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/health/health.ts new file mode 100644 index 0000000000..3ea4b5be7b --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/health/health.ts @@ -0,0 +1,187 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import { useQuery } from "@tanstack/react-query"; +import type { + DataTag, + DefinedInitialDataOptions, + DefinedUseQueryResult, + QueryClient, + QueryFunction, + QueryKey, + UndefinedInitialDataOptions, + UseQueryOptions, + UseQueryResult, +} from "@tanstack/react-query"; + +import { customMutator } from "../../../mutators/custom-mutator"; + +type SecondParameter unknown> = Parameters[1]; + +/** + * @summary Health + */ +export type getHealthHealthResponse200 = { + data: unknown; + status: 200; +}; + +export type getHealthHealthResponseComposite = getHealthHealthResponse200; + +export type getHealthHealthResponse = getHealthHealthResponseComposite & { + headers: Headers; +}; + +export const getGetHealthHealthUrl = () => { + return `/health`; +}; + +export const getHealthHealth = async ( + options?: RequestInit, +): Promise => { + return customMutator(getGetHealthHealthUrl(), { + ...options, + method: "GET", + }); +}; + +export const getGetHealthHealthQueryKey = () => { + return [`/health`] as const; +}; + +export const getGetHealthHealthQueryOptions = < + TData = Awaited>, + TError = unknown, +>(options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; +}) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getGetHealthHealthQueryKey(); + + const queryFn: QueryFunction>> = ({ + signal, + }) => getHealthHealth({ signal, ...requestOptions }); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetHealthHealthQueryResult = NonNullable< + Awaited> +>; +export type GetHealthHealthQueryError = unknown; + +export function useGetHealthHealth< + TData = Awaited>, + TError = unknown, +>( + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetHealthHealth< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetHealthHealth< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Health + */ + +export function useGetHealthHealth< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetHealthHealthQueryOptions(options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/endpoints/integrations/integrations.ts b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/integrations/integrations.ts new file mode 100644 index 0000000000..bff97a64d4 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/integrations/integrations.ts @@ -0,0 +1,1423 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import { useMutation, useQuery } from "@tanstack/react-query"; +import type { + DataTag, + DefinedInitialDataOptions, + DefinedUseQueryResult, + MutationFunction, + QueryClient, + QueryFunction, + QueryKey, + UndefinedInitialDataOptions, + UseMutationOptions, + UseMutationResult, + UseQueryOptions, + UseQueryResult, +} from "@tanstack/react-query"; + +import type { BodyPostV1Callback } from "../../models/bodyPostV1Callback"; + +import type { CredentialsMetaResponse } from "../../models/credentialsMetaResponse"; + +import type { DeleteV1DeleteCredentials200 } from "../../models/deleteV1DeleteCredentials200"; + +import type { DeleteV1DeleteCredentialsParams } from "../../models/deleteV1DeleteCredentialsParams"; + +import type { GetV1GetCredential200 } from "../../models/getV1GetCredential200"; + +import type { GetV1LoginParams } from "../../models/getV1LoginParams"; + +import type { HTTPValidationError } from "../../models/hTTPValidationError"; + +import type { LoginResponse } from "../../models/loginResponse"; + +import type { PostV1CreateCredentials201 } from "../../models/postV1CreateCredentials201"; + +import type { PostV1CreateCredentialsBody } from "../../models/postV1CreateCredentialsBody"; + +import type { ProviderName } from "../../models/providerName"; + +import { customMutator } from "../../../mutators/custom-mutator"; + +type SecondParameter unknown> = Parameters[1]; + +/** + * @summary Login + */ +export type getV1LoginResponse200 = { + data: LoginResponse; + status: 200; +}; + +export type getV1LoginResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type getV1LoginResponseComposite = + | getV1LoginResponse200 + | getV1LoginResponse422; + +export type getV1LoginResponse = getV1LoginResponseComposite & { + headers: Headers; +}; + +export const getGetV1LoginUrl = ( + provider: ProviderName, + params?: GetV1LoginParams, +) => { + const normalizedParams = new URLSearchParams(); + + Object.entries(params || {}).forEach(([key, value]) => { + if (value !== undefined) { + normalizedParams.append(key, value === null ? "null" : value.toString()); + } + }); + + const stringifiedParams = normalizedParams.toString(); + + return stringifiedParams.length > 0 + ? `/api/integrations/${provider}/login?${stringifiedParams}` + : `/api/integrations/${provider}/login`; +}; + +export const getV1Login = async ( + provider: ProviderName, + params?: GetV1LoginParams, + options?: RequestInit, +): Promise => { + return customMutator(getGetV1LoginUrl(provider, params), { + ...options, + method: "GET", + }); +}; + +export const getGetV1LoginQueryKey = ( + provider: ProviderName, + params?: GetV1LoginParams, +) => { + return [ + `/api/integrations/${provider}/login`, + ...(params ? [params] : []), + ] as const; +}; + +export const getGetV1LoginQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + provider: ProviderName, + params?: GetV1LoginParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetV1LoginQueryKey(provider, params); + + const queryFn: QueryFunction>> = ({ + signal, + }) => getV1Login(provider, params, { signal, ...requestOptions }); + + return { + queryKey, + queryFn, + enabled: !!provider, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV1LoginQueryResult = NonNullable< + Awaited> +>; +export type GetV1LoginQueryError = HTTPValidationError; + +export function useGetV1Login< + TData = Awaited>, + TError = HTTPValidationError, +>( + provider: ProviderName, + params: undefined | GetV1LoginParams, + options: { + query: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1Login< + TData = Awaited>, + TError = HTTPValidationError, +>( + provider: ProviderName, + params?: GetV1LoginParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1Login< + TData = Awaited>, + TError = HTTPValidationError, +>( + provider: ProviderName, + params?: GetV1LoginParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Login + */ + +export function useGetV1Login< + TData = Awaited>, + TError = HTTPValidationError, +>( + provider: ProviderName, + params?: GetV1LoginParams, + options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV1LoginQueryOptions(provider, params, options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Callback + */ +export type postV1CallbackResponse200 = { + data: CredentialsMetaResponse; + status: 200; +}; + +export type postV1CallbackResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV1CallbackResponseComposite = + | postV1CallbackResponse200 + | postV1CallbackResponse422; + +export type postV1CallbackResponse = postV1CallbackResponseComposite & { + headers: Headers; +}; + +export const getPostV1CallbackUrl = (provider: ProviderName) => { + return `/api/integrations/${provider}/callback`; +}; + +export const postV1Callback = async ( + provider: ProviderName, + bodyPostV1Callback: BodyPostV1Callback, + options?: RequestInit, +): Promise => { + return customMutator(getPostV1CallbackUrl(provider), { + ...options, + method: "POST", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(bodyPostV1Callback), + }); +}; + +export const getPostV1CallbackMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { provider: ProviderName; data: BodyPostV1Callback }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { provider: ProviderName; data: BodyPostV1Callback }, + TContext +> => { + const mutationKey = ["postV1Callback"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { provider: ProviderName; data: BodyPostV1Callback } + > = (props) => { + const { provider, data } = props ?? {}; + + return postV1Callback(provider, data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV1CallbackMutationResult = NonNullable< + Awaited> +>; +export type PostV1CallbackMutationBody = BodyPostV1Callback; +export type PostV1CallbackMutationError = HTTPValidationError; + +/** + * @summary Callback + */ +export const usePostV1Callback = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { provider: ProviderName; data: BodyPostV1Callback }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { provider: ProviderName; data: BodyPostV1Callback }, + TContext +> => { + const mutationOptions = getPostV1CallbackMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * @summary List Credentials + */ +export type getV1ListCredentialsResponse200 = { + data: CredentialsMetaResponse[]; + status: 200; +}; + +export type getV1ListCredentialsResponseComposite = + getV1ListCredentialsResponse200; + +export type getV1ListCredentialsResponse = + getV1ListCredentialsResponseComposite & { + headers: Headers; + }; + +export const getGetV1ListCredentialsUrl = () => { + return `/api/integrations/credentials`; +}; + +export const getV1ListCredentials = async ( + options?: RequestInit, +): Promise => { + return customMutator( + getGetV1ListCredentialsUrl(), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV1ListCredentialsQueryKey = () => { + return [`/api/integrations/credentials`] as const; +}; + +export const getGetV1ListCredentialsQueryOptions = < + TData = Awaited>, + TError = unknown, +>(options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; +}) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getGetV1ListCredentialsQueryKey(); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => getV1ListCredentials({ signal, ...requestOptions }); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV1ListCredentialsQueryResult = NonNullable< + Awaited> +>; +export type GetV1ListCredentialsQueryError = unknown; + +export function useGetV1ListCredentials< + TData = Awaited>, + TError = unknown, +>( + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1ListCredentials< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1ListCredentials< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary List Credentials + */ + +export function useGetV1ListCredentials< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV1ListCredentialsQueryOptions(options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary List Credentials By Provider + */ +export type getV1ListCredentialsByProviderResponse200 = { + data: CredentialsMetaResponse[]; + status: 200; +}; + +export type getV1ListCredentialsByProviderResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type getV1ListCredentialsByProviderResponseComposite = + | getV1ListCredentialsByProviderResponse200 + | getV1ListCredentialsByProviderResponse422; + +export type getV1ListCredentialsByProviderResponse = + getV1ListCredentialsByProviderResponseComposite & { + headers: Headers; + }; + +export const getGetV1ListCredentialsByProviderUrl = ( + provider: ProviderName, +) => { + return `/api/integrations/${provider}/credentials`; +}; + +export const getV1ListCredentialsByProvider = async ( + provider: ProviderName, + options?: RequestInit, +): Promise => { + return customMutator( + getGetV1ListCredentialsByProviderUrl(provider), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV1ListCredentialsByProviderQueryKey = ( + provider: ProviderName, +) => { + return [`/api/integrations/${provider}/credentials`] as const; +}; + +export const getGetV1ListCredentialsByProviderQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + provider: ProviderName, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? + getGetV1ListCredentialsByProviderQueryKey(provider); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV1ListCredentialsByProvider(provider, { signal, ...requestOptions }); + + return { + queryKey, + queryFn, + enabled: !!provider, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV1ListCredentialsByProviderQueryResult = NonNullable< + Awaited> +>; +export type GetV1ListCredentialsByProviderQueryError = HTTPValidationError; + +export function useGetV1ListCredentialsByProvider< + TData = Awaited>, + TError = HTTPValidationError, +>( + provider: ProviderName, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1ListCredentialsByProvider< + TData = Awaited>, + TError = HTTPValidationError, +>( + provider: ProviderName, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1ListCredentialsByProvider< + TData = Awaited>, + TError = HTTPValidationError, +>( + provider: ProviderName, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary List Credentials By Provider + */ + +export function useGetV1ListCredentialsByProvider< + TData = Awaited>, + TError = HTTPValidationError, +>( + provider: ProviderName, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV1ListCredentialsByProviderQueryOptions( + provider, + options, + ); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Create Credentials + */ +export type postV1CreateCredentialsResponse201 = { + data: PostV1CreateCredentials201; + status: 201; +}; + +export type postV1CreateCredentialsResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV1CreateCredentialsResponseComposite = + | postV1CreateCredentialsResponse201 + | postV1CreateCredentialsResponse422; + +export type postV1CreateCredentialsResponse = + postV1CreateCredentialsResponseComposite & { + headers: Headers; + }; + +export const getPostV1CreateCredentialsUrl = (provider: ProviderName) => { + return `/api/integrations/${provider}/credentials`; +}; + +export const postV1CreateCredentials = async ( + provider: ProviderName, + postV1CreateCredentialsBody: PostV1CreateCredentialsBody, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV1CreateCredentialsUrl(provider), + { + ...options, + method: "POST", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(postV1CreateCredentialsBody), + }, + ); +}; + +export const getPostV1CreateCredentialsMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { provider: ProviderName; data: PostV1CreateCredentialsBody }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { provider: ProviderName; data: PostV1CreateCredentialsBody }, + TContext +> => { + const mutationKey = ["postV1CreateCredentials"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { provider: ProviderName; data: PostV1CreateCredentialsBody } + > = (props) => { + const { provider, data } = props ?? {}; + + return postV1CreateCredentials(provider, data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV1CreateCredentialsMutationResult = NonNullable< + Awaited> +>; +export type PostV1CreateCredentialsMutationBody = PostV1CreateCredentialsBody; +export type PostV1CreateCredentialsMutationError = HTTPValidationError; + +/** + * @summary Create Credentials + */ +export const usePostV1CreateCredentials = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { provider: ProviderName; data: PostV1CreateCredentialsBody }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { provider: ProviderName; data: PostV1CreateCredentialsBody }, + TContext +> => { + const mutationOptions = getPostV1CreateCredentialsMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * @summary Get Credential + */ +export type getV1GetCredentialResponse200 = { + data: GetV1GetCredential200; + status: 200; +}; + +export type getV1GetCredentialResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type getV1GetCredentialResponseComposite = + | getV1GetCredentialResponse200 + | getV1GetCredentialResponse422; + +export type getV1GetCredentialResponse = getV1GetCredentialResponseComposite & { + headers: Headers; +}; + +export const getGetV1GetCredentialUrl = ( + provider: ProviderName, + credId: string, +) => { + return `/api/integrations/${provider}/credentials/${credId}`; +}; + +export const getV1GetCredential = async ( + provider: ProviderName, + credId: string, + options?: RequestInit, +): Promise => { + return customMutator( + getGetV1GetCredentialUrl(provider, credId), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV1GetCredentialQueryKey = ( + provider: ProviderName, + credId: string, +) => { + return [`/api/integrations/${provider}/credentials/${credId}`] as const; +}; + +export const getGetV1GetCredentialQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + provider: ProviderName, + credId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetV1GetCredentialQueryKey(provider, credId); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV1GetCredential(provider, credId, { signal, ...requestOptions }); + + return { + queryKey, + queryFn, + enabled: !!(provider && credId), + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV1GetCredentialQueryResult = NonNullable< + Awaited> +>; +export type GetV1GetCredentialQueryError = HTTPValidationError; + +export function useGetV1GetCredential< + TData = Awaited>, + TError = HTTPValidationError, +>( + provider: ProviderName, + credId: string, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetCredential< + TData = Awaited>, + TError = HTTPValidationError, +>( + provider: ProviderName, + credId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetCredential< + TData = Awaited>, + TError = HTTPValidationError, +>( + provider: ProviderName, + credId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Get Credential + */ + +export function useGetV1GetCredential< + TData = Awaited>, + TError = HTTPValidationError, +>( + provider: ProviderName, + credId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV1GetCredentialQueryOptions( + provider, + credId, + options, + ); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Delete Credentials + */ +export type deleteV1DeleteCredentialsResponse200 = { + data: DeleteV1DeleteCredentials200; + status: 200; +}; + +export type deleteV1DeleteCredentialsResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type deleteV1DeleteCredentialsResponseComposite = + | deleteV1DeleteCredentialsResponse200 + | deleteV1DeleteCredentialsResponse422; + +export type deleteV1DeleteCredentialsResponse = + deleteV1DeleteCredentialsResponseComposite & { + headers: Headers; + }; + +export const getDeleteV1DeleteCredentialsUrl = ( + provider: ProviderName, + credId: string, + params?: DeleteV1DeleteCredentialsParams, +) => { + const normalizedParams = new URLSearchParams(); + + Object.entries(params || {}).forEach(([key, value]) => { + if (value !== undefined) { + normalizedParams.append(key, value === null ? "null" : value.toString()); + } + }); + + const stringifiedParams = normalizedParams.toString(); + + return stringifiedParams.length > 0 + ? `/api/integrations/${provider}/credentials/${credId}?${stringifiedParams}` + : `/api/integrations/${provider}/credentials/${credId}`; +}; + +export const deleteV1DeleteCredentials = async ( + provider: ProviderName, + credId: string, + params?: DeleteV1DeleteCredentialsParams, + options?: RequestInit, +): Promise => { + return customMutator( + getDeleteV1DeleteCredentialsUrl(provider, credId, params), + { + ...options, + method: "DELETE", + }, + ); +}; + +export const getDeleteV1DeleteCredentialsMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { + provider: ProviderName; + credId: string; + params?: DeleteV1DeleteCredentialsParams; + }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { + provider: ProviderName; + credId: string; + params?: DeleteV1DeleteCredentialsParams; + }, + TContext +> => { + const mutationKey = ["deleteV1DeleteCredentials"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { + provider: ProviderName; + credId: string; + params?: DeleteV1DeleteCredentialsParams; + } + > = (props) => { + const { provider, credId, params } = props ?? {}; + + return deleteV1DeleteCredentials(provider, credId, params, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type DeleteV1DeleteCredentialsMutationResult = NonNullable< + Awaited> +>; + +export type DeleteV1DeleteCredentialsMutationError = HTTPValidationError; + +/** + * @summary Delete Credentials + */ +export const useDeleteV1DeleteCredentials = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { + provider: ProviderName; + credId: string; + params?: DeleteV1DeleteCredentialsParams; + }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { + provider: ProviderName; + credId: string; + params?: DeleteV1DeleteCredentialsParams; + }, + TContext +> => { + const mutationOptions = getDeleteV1DeleteCredentialsMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * @summary Webhook Ingress Generic + */ +export type postV1WebhookIngressGenericResponse200 = { + data: unknown; + status: 200; +}; + +export type postV1WebhookIngressGenericResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV1WebhookIngressGenericResponseComposite = + | postV1WebhookIngressGenericResponse200 + | postV1WebhookIngressGenericResponse422; + +export type postV1WebhookIngressGenericResponse = + postV1WebhookIngressGenericResponseComposite & { + headers: Headers; + }; + +export const getPostV1WebhookIngressGenericUrl = ( + provider: ProviderName, + webhookId: string, +) => { + return `/api/integrations/${provider}/webhooks/${webhookId}/ingress`; +}; + +export const postV1WebhookIngressGeneric = async ( + provider: ProviderName, + webhookId: string, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV1WebhookIngressGenericUrl(provider, webhookId), + { + ...options, + method: "POST", + }, + ); +}; + +export const getPostV1WebhookIngressGenericMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { provider: ProviderName; webhookId: string }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { provider: ProviderName; webhookId: string }, + TContext +> => { + const mutationKey = ["postV1WebhookIngressGeneric"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { provider: ProviderName; webhookId: string } + > = (props) => { + const { provider, webhookId } = props ?? {}; + + return postV1WebhookIngressGeneric(provider, webhookId, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV1WebhookIngressGenericMutationResult = NonNullable< + Awaited> +>; + +export type PostV1WebhookIngressGenericMutationError = HTTPValidationError; + +/** + * @summary Webhook Ingress Generic + */ +export const usePostV1WebhookIngressGeneric = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { provider: ProviderName; webhookId: string }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { provider: ProviderName; webhookId: string }, + TContext +> => { + const mutationOptions = + getPostV1WebhookIngressGenericMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * @summary Webhook Ping + */ +export type postV1WebhookPingResponse200 = { + data: unknown; + status: 200; +}; + +export type postV1WebhookPingResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV1WebhookPingResponseComposite = + | postV1WebhookPingResponse200 + | postV1WebhookPingResponse422; + +export type postV1WebhookPingResponse = postV1WebhookPingResponseComposite & { + headers: Headers; +}; + +export const getPostV1WebhookPingUrl = (webhookId: string) => { + return `/api/integrations/webhooks/${webhookId}/ping`; +}; + +export const postV1WebhookPing = async ( + webhookId: string, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV1WebhookPingUrl(webhookId), + { + ...options, + method: "POST", + }, + ); +}; + +export const getPostV1WebhookPingMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { webhookId: string }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { webhookId: string }, + TContext +> => { + const mutationKey = ["postV1WebhookPing"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { webhookId: string } + > = (props) => { + const { webhookId } = props ?? {}; + + return postV1WebhookPing(webhookId, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV1WebhookPingMutationResult = NonNullable< + Awaited> +>; + +export type PostV1WebhookPingMutationError = HTTPValidationError; + +/** + * @summary Webhook Ping + */ +export const usePostV1WebhookPing = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { webhookId: string }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { webhookId: string }, + TContext +> => { + const mutationOptions = getPostV1WebhookPingMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/endpoints/library/library.ts b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/library/library.ts new file mode 100644 index 0000000000..6560be16d7 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/library/library.ts @@ -0,0 +1,1704 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import { useInfiniteQuery, useMutation, useQuery } from "@tanstack/react-query"; +import type { + DataTag, + DefinedInitialDataOptions, + DefinedUseInfiniteQueryResult, + DefinedUseQueryResult, + InfiniteData, + MutationFunction, + QueryClient, + QueryFunction, + QueryKey, + UndefinedInitialDataOptions, + UseInfiniteQueryOptions, + UseInfiniteQueryResult, + UseMutationOptions, + UseMutationResult, + UseQueryOptions, + UseQueryResult, +} from "@tanstack/react-query"; + +import type { BodyPostV2AddMarketplaceAgent } from "../../models/bodyPostV2AddMarketplaceAgent"; + +import type { GetV2GetAgentByStoreId200 } from "../../models/getV2GetAgentByStoreId200"; + +import type { GetV2GetLibraryAgentByGraphIdParams } from "../../models/getV2GetLibraryAgentByGraphIdParams"; + +import type { GetV2ListLibraryAgentsParams } from "../../models/getV2ListLibraryAgentsParams"; + +import type { HTTPValidationError } from "../../models/hTTPValidationError"; + +import type { LibraryAgent } from "../../models/libraryAgent"; + +import type { LibraryAgentPreset } from "../../models/libraryAgentPreset"; + +import type { LibraryAgentResponse } from "../../models/libraryAgentResponse"; + +import type { LibraryAgentUpdateRequest } from "../../models/libraryAgentUpdateRequest"; + +import type { TriggeredPresetSetupParams } from "../../models/triggeredPresetSetupParams"; + +import { customMutator } from "../../../mutators/custom-mutator"; + +type SecondParameter unknown> = Parameters[1]; + +/** + * Get all agents in the user's library (both created and saved). + +Args: + user_id: ID of the authenticated user. + search_term: Optional search term to filter agents by name/description. + filter_by: List of filters to apply (favorites, created by user). + sort_by: List of sorting criteria (created date, updated date). + page: Page number to retrieve. + page_size: Number of agents per page. + +Returns: + A LibraryAgentResponse containing agents and pagination metadata. + +Raises: + HTTPException: If a server/database error occurs. + * @summary List Library Agents + */ +export type getV2ListLibraryAgentsResponse200 = { + data: LibraryAgentResponse; + status: 200; +}; + +export type getV2ListLibraryAgentsResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type getV2ListLibraryAgentsResponseComposite = + | getV2ListLibraryAgentsResponse200 + | getV2ListLibraryAgentsResponse422; + +export type getV2ListLibraryAgentsResponse = + getV2ListLibraryAgentsResponseComposite & { + headers: Headers; + }; + +export const getGetV2ListLibraryAgentsUrl = ( + params?: GetV2ListLibraryAgentsParams, +) => { + const normalizedParams = new URLSearchParams(); + + Object.entries(params || {}).forEach(([key, value]) => { + if (value !== undefined) { + normalizedParams.append(key, value === null ? "null" : value.toString()); + } + }); + + const stringifiedParams = normalizedParams.toString(); + + return stringifiedParams.length > 0 + ? `/api/library/agents?${stringifiedParams}` + : `/api/library/agents`; +}; + +export const getV2ListLibraryAgents = async ( + params?: GetV2ListLibraryAgentsParams, + options?: RequestInit, +): Promise => { + return customMutator( + getGetV2ListLibraryAgentsUrl(params), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV2ListLibraryAgentsQueryKey = ( + params?: GetV2ListLibraryAgentsParams, +) => { + return [`/api/library/agents`, ...(params ? [params] : [])] as const; +}; + +export const getGetV2ListLibraryAgentsInfiniteQueryOptions = < + TData = InfiniteData< + Awaited>, + GetV2ListLibraryAgentsParams["page"] + >, + TError = HTTPValidationError, +>( + params?: GetV2ListLibraryAgentsParams, + options?: { + query?: Partial< + UseInfiniteQueryOptions< + Awaited>, + TError, + TData, + QueryKey, + GetV2ListLibraryAgentsParams["page"] + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetV2ListLibraryAgentsQueryKey(params); + + const queryFn: QueryFunction< + Awaited>, + QueryKey, + GetV2ListLibraryAgentsParams["page"] + > = ({ signal, pageParam }) => + getV2ListLibraryAgents( + { ...params, page: pageParam || params?.["page"] }, + { signal, ...requestOptions }, + ); + + return { queryKey, queryFn, ...queryOptions } as UseInfiniteQueryOptions< + Awaited>, + TError, + TData, + QueryKey, + GetV2ListLibraryAgentsParams["page"] + > & { queryKey: DataTag }; +}; + +export type GetV2ListLibraryAgentsInfiniteQueryResult = NonNullable< + Awaited> +>; +export type GetV2ListLibraryAgentsInfiniteQueryError = HTTPValidationError; + +export function useGetV2ListLibraryAgentsInfinite< + TData = InfiniteData< + Awaited>, + GetV2ListLibraryAgentsParams["page"] + >, + TError = HTTPValidationError, +>( + params: undefined | GetV2ListLibraryAgentsParams, + options: { + query: Partial< + UseInfiniteQueryOptions< + Awaited>, + TError, + TData, + QueryKey, + GetV2ListLibraryAgentsParams["page"] + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited>, + QueryKey + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseInfiniteQueryResult & { + queryKey: DataTag; +}; +export function useGetV2ListLibraryAgentsInfinite< + TData = InfiniteData< + Awaited>, + GetV2ListLibraryAgentsParams["page"] + >, + TError = HTTPValidationError, +>( + params?: GetV2ListLibraryAgentsParams, + options?: { + query?: Partial< + UseInfiniteQueryOptions< + Awaited>, + TError, + TData, + QueryKey, + GetV2ListLibraryAgentsParams["page"] + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited>, + QueryKey + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseInfiniteQueryResult & { + queryKey: DataTag; +}; +export function useGetV2ListLibraryAgentsInfinite< + TData = InfiniteData< + Awaited>, + GetV2ListLibraryAgentsParams["page"] + >, + TError = HTTPValidationError, +>( + params?: GetV2ListLibraryAgentsParams, + options?: { + query?: Partial< + UseInfiniteQueryOptions< + Awaited>, + TError, + TData, + QueryKey, + GetV2ListLibraryAgentsParams["page"] + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseInfiniteQueryResult & { + queryKey: DataTag; +}; +/** + * @summary List Library Agents + */ + +export function useGetV2ListLibraryAgentsInfinite< + TData = InfiniteData< + Awaited>, + GetV2ListLibraryAgentsParams["page"] + >, + TError = HTTPValidationError, +>( + params?: GetV2ListLibraryAgentsParams, + options?: { + query?: Partial< + UseInfiniteQueryOptions< + Awaited>, + TError, + TData, + QueryKey, + GetV2ListLibraryAgentsParams["page"] + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseInfiniteQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV2ListLibraryAgentsInfiniteQueryOptions( + params, + options, + ); + + const query = useInfiniteQuery( + queryOptions, + queryClient, + ) as UseInfiniteQueryResult & { + queryKey: DataTag; + }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +export const getGetV2ListLibraryAgentsQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: GetV2ListLibraryAgentsParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetV2ListLibraryAgentsQueryKey(params); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV2ListLibraryAgents(params, { signal, ...requestOptions }); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV2ListLibraryAgentsQueryResult = NonNullable< + Awaited> +>; +export type GetV2ListLibraryAgentsQueryError = HTTPValidationError; + +export function useGetV2ListLibraryAgents< + TData = Awaited>, + TError = HTTPValidationError, +>( + params: undefined | GetV2ListLibraryAgentsParams, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2ListLibraryAgents< + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: GetV2ListLibraryAgentsParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2ListLibraryAgents< + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: GetV2ListLibraryAgentsParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary List Library Agents + */ + +export function useGetV2ListLibraryAgents< + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: GetV2ListLibraryAgentsParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV2ListLibraryAgentsQueryOptions(params, options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * Add an agent from the marketplace to the user's library. + +Args: + store_listing_version_id: ID of the store listing version to add. + user_id: ID of the authenticated user. + +Returns: + library_model.LibraryAgent: Agent added to the library + +Raises: + HTTPException(404): If the listing version is not found. + HTTPException(500): If a server/database error occurs. + * @summary Add Marketplace Agent + */ +export type postV2AddMarketplaceAgentResponse201 = { + data: LibraryAgent; + status: 201; +}; + +export type postV2AddMarketplaceAgentResponse404 = { + data: void; + status: 404; +}; + +export type postV2AddMarketplaceAgentResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV2AddMarketplaceAgentResponseComposite = + | postV2AddMarketplaceAgentResponse201 + | postV2AddMarketplaceAgentResponse404 + | postV2AddMarketplaceAgentResponse422; + +export type postV2AddMarketplaceAgentResponse = + postV2AddMarketplaceAgentResponseComposite & { + headers: Headers; + }; + +export const getPostV2AddMarketplaceAgentUrl = () => { + return `/api/library/agents`; +}; + +export const postV2AddMarketplaceAgent = async ( + bodyPostV2AddMarketplaceAgent: BodyPostV2AddMarketplaceAgent, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV2AddMarketplaceAgentUrl(), + { + ...options, + method: "POST", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(bodyPostV2AddMarketplaceAgent), + }, + ); +}; + +export const getPostV2AddMarketplaceAgentMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: BodyPostV2AddMarketplaceAgent }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { data: BodyPostV2AddMarketplaceAgent }, + TContext +> => { + const mutationKey = ["postV2AddMarketplaceAgent"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { data: BodyPostV2AddMarketplaceAgent } + > = (props) => { + const { data } = props ?? {}; + + return postV2AddMarketplaceAgent(data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV2AddMarketplaceAgentMutationResult = NonNullable< + Awaited> +>; +export type PostV2AddMarketplaceAgentMutationBody = + BodyPostV2AddMarketplaceAgent; +export type PostV2AddMarketplaceAgentMutationError = void | HTTPValidationError; + +/** + * @summary Add Marketplace Agent + */ +export const usePostV2AddMarketplaceAgent = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: BodyPostV2AddMarketplaceAgent }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { data: BodyPostV2AddMarketplaceAgent }, + TContext +> => { + const mutationOptions = getPostV2AddMarketplaceAgentMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * @summary Get Library Agent + */ +export type getV2GetLibraryAgentResponse200 = { + data: LibraryAgent; + status: 200; +}; + +export type getV2GetLibraryAgentResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type getV2GetLibraryAgentResponseComposite = + | getV2GetLibraryAgentResponse200 + | getV2GetLibraryAgentResponse422; + +export type getV2GetLibraryAgentResponse = + getV2GetLibraryAgentResponseComposite & { + headers: Headers; + }; + +export const getGetV2GetLibraryAgentUrl = (libraryAgentId: string) => { + return `/api/library/agents/${libraryAgentId}`; +}; + +export const getV2GetLibraryAgent = async ( + libraryAgentId: string, + options?: RequestInit, +): Promise => { + return customMutator( + getGetV2GetLibraryAgentUrl(libraryAgentId), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV2GetLibraryAgentQueryKey = (libraryAgentId: string) => { + return [`/api/library/agents/${libraryAgentId}`] as const; +}; + +export const getGetV2GetLibraryAgentQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + libraryAgentId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetV2GetLibraryAgentQueryKey(libraryAgentId); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV2GetLibraryAgent(libraryAgentId, { signal, ...requestOptions }); + + return { + queryKey, + queryFn, + enabled: !!libraryAgentId, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV2GetLibraryAgentQueryResult = NonNullable< + Awaited> +>; +export type GetV2GetLibraryAgentQueryError = HTTPValidationError; + +export function useGetV2GetLibraryAgent< + TData = Awaited>, + TError = HTTPValidationError, +>( + libraryAgentId: string, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2GetLibraryAgent< + TData = Awaited>, + TError = HTTPValidationError, +>( + libraryAgentId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2GetLibraryAgent< + TData = Awaited>, + TError = HTTPValidationError, +>( + libraryAgentId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Get Library Agent + */ + +export function useGetV2GetLibraryAgent< + TData = Awaited>, + TError = HTTPValidationError, +>( + libraryAgentId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV2GetLibraryAgentQueryOptions( + libraryAgentId, + options, + ); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * Update the library agent with the given fields. + +Args: + library_agent_id: ID of the library agent to update. + payload: Fields to update (auto_update_version, is_favorite, etc.). + user_id: ID of the authenticated user. + +Raises: + HTTPException(500): If a server/database error occurs. + * @summary Update Library Agent + */ +export type patchV2UpdateLibraryAgentResponse200 = { + data: LibraryAgent; + status: 200; +}; + +export type patchV2UpdateLibraryAgentResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type patchV2UpdateLibraryAgentResponse500 = { + data: void; + status: 500; +}; + +export type patchV2UpdateLibraryAgentResponseComposite = + | patchV2UpdateLibraryAgentResponse200 + | patchV2UpdateLibraryAgentResponse422 + | patchV2UpdateLibraryAgentResponse500; + +export type patchV2UpdateLibraryAgentResponse = + patchV2UpdateLibraryAgentResponseComposite & { + headers: Headers; + }; + +export const getPatchV2UpdateLibraryAgentUrl = (libraryAgentId: string) => { + return `/api/library/agents/${libraryAgentId}`; +}; + +export const patchV2UpdateLibraryAgent = async ( + libraryAgentId: string, + libraryAgentUpdateRequest: LibraryAgentUpdateRequest, + options?: RequestInit, +): Promise => { + return customMutator( + getPatchV2UpdateLibraryAgentUrl(libraryAgentId), + { + ...options, + method: "PATCH", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(libraryAgentUpdateRequest), + }, + ); +}; + +export const getPatchV2UpdateLibraryAgentMutationOptions = < + TError = HTTPValidationError | void, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { libraryAgentId: string; data: LibraryAgentUpdateRequest }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { libraryAgentId: string; data: LibraryAgentUpdateRequest }, + TContext +> => { + const mutationKey = ["patchV2UpdateLibraryAgent"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { libraryAgentId: string; data: LibraryAgentUpdateRequest } + > = (props) => { + const { libraryAgentId, data } = props ?? {}; + + return patchV2UpdateLibraryAgent(libraryAgentId, data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PatchV2UpdateLibraryAgentMutationResult = NonNullable< + Awaited> +>; +export type PatchV2UpdateLibraryAgentMutationBody = LibraryAgentUpdateRequest; +export type PatchV2UpdateLibraryAgentMutationError = HTTPValidationError | void; + +/** + * @summary Update Library Agent + */ +export const usePatchV2UpdateLibraryAgent = < + TError = HTTPValidationError | void, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { libraryAgentId: string; data: LibraryAgentUpdateRequest }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { libraryAgentId: string; data: LibraryAgentUpdateRequest }, + TContext +> => { + const mutationOptions = getPatchV2UpdateLibraryAgentMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * Soft-delete the specified library agent. + +Args: + library_agent_id: ID of the library agent to delete. + user_id: ID of the authenticated user. + +Returns: + 204 No Content if successful. + +Raises: + HTTPException(404): If the agent does not exist. + HTTPException(500): If a server/database error occurs. + * @summary Delete Library Agent + */ +export type deleteV2DeleteLibraryAgentResponse200 = { + data: unknown; + status: 200; +}; + +export type deleteV2DeleteLibraryAgentResponse204 = { + data: void; + status: 204; +}; + +export type deleteV2DeleteLibraryAgentResponse404 = { + data: void; + status: 404; +}; + +export type deleteV2DeleteLibraryAgentResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type deleteV2DeleteLibraryAgentResponseComposite = + | deleteV2DeleteLibraryAgentResponse200 + | deleteV2DeleteLibraryAgentResponse204 + | deleteV2DeleteLibraryAgentResponse404 + | deleteV2DeleteLibraryAgentResponse422; + +export type deleteV2DeleteLibraryAgentResponse = + deleteV2DeleteLibraryAgentResponseComposite & { + headers: Headers; + }; + +export const getDeleteV2DeleteLibraryAgentUrl = (libraryAgentId: string) => { + return `/api/library/agents/${libraryAgentId}`; +}; + +export const deleteV2DeleteLibraryAgent = async ( + libraryAgentId: string, + options?: RequestInit, +): Promise => { + return customMutator( + getDeleteV2DeleteLibraryAgentUrl(libraryAgentId), + { + ...options, + method: "DELETE", + }, + ); +}; + +export const getDeleteV2DeleteLibraryAgentMutationOptions = < + TError = void | HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { libraryAgentId: string }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { libraryAgentId: string }, + TContext +> => { + const mutationKey = ["deleteV2DeleteLibraryAgent"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { libraryAgentId: string } + > = (props) => { + const { libraryAgentId } = props ?? {}; + + return deleteV2DeleteLibraryAgent(libraryAgentId, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type DeleteV2DeleteLibraryAgentMutationResult = NonNullable< + Awaited> +>; + +export type DeleteV2DeleteLibraryAgentMutationError = + void | HTTPValidationError; + +/** + * @summary Delete Library Agent + */ +export const useDeleteV2DeleteLibraryAgent = < + TError = void | HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { libraryAgentId: string }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { libraryAgentId: string }, + TContext +> => { + const mutationOptions = getDeleteV2DeleteLibraryAgentMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * @summary Get Library Agent By Graph Id + */ +export type getV2GetLibraryAgentByGraphIdResponse200 = { + data: LibraryAgent; + status: 200; +}; + +export type getV2GetLibraryAgentByGraphIdResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type getV2GetLibraryAgentByGraphIdResponseComposite = + | getV2GetLibraryAgentByGraphIdResponse200 + | getV2GetLibraryAgentByGraphIdResponse422; + +export type getV2GetLibraryAgentByGraphIdResponse = + getV2GetLibraryAgentByGraphIdResponseComposite & { + headers: Headers; + }; + +export const getGetV2GetLibraryAgentByGraphIdUrl = ( + graphId: string, + params?: GetV2GetLibraryAgentByGraphIdParams, +) => { + const normalizedParams = new URLSearchParams(); + + Object.entries(params || {}).forEach(([key, value]) => { + if (value !== undefined) { + normalizedParams.append(key, value === null ? "null" : value.toString()); + } + }); + + const stringifiedParams = normalizedParams.toString(); + + return stringifiedParams.length > 0 + ? `/api/library/agents/by-graph/${graphId}?${stringifiedParams}` + : `/api/library/agents/by-graph/${graphId}`; +}; + +export const getV2GetLibraryAgentByGraphId = async ( + graphId: string, + params?: GetV2GetLibraryAgentByGraphIdParams, + options?: RequestInit, +): Promise => { + return customMutator( + getGetV2GetLibraryAgentByGraphIdUrl(graphId, params), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV2GetLibraryAgentByGraphIdQueryKey = ( + graphId: string, + params?: GetV2GetLibraryAgentByGraphIdParams, +) => { + return [ + `/api/library/agents/by-graph/${graphId}`, + ...(params ? [params] : []), + ] as const; +}; + +export const getGetV2GetLibraryAgentByGraphIdQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + params?: GetV2GetLibraryAgentByGraphIdParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? + getGetV2GetLibraryAgentByGraphIdQueryKey(graphId, params); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV2GetLibraryAgentByGraphId(graphId, params, { + signal, + ...requestOptions, + }); + + return { + queryKey, + queryFn, + enabled: !!graphId, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV2GetLibraryAgentByGraphIdQueryResult = NonNullable< + Awaited> +>; +export type GetV2GetLibraryAgentByGraphIdQueryError = HTTPValidationError; + +export function useGetV2GetLibraryAgentByGraphId< + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + params: undefined | GetV2GetLibraryAgentByGraphIdParams, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2GetLibraryAgentByGraphId< + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + params?: GetV2GetLibraryAgentByGraphIdParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2GetLibraryAgentByGraphId< + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + params?: GetV2GetLibraryAgentByGraphIdParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Get Library Agent By Graph Id + */ + +export function useGetV2GetLibraryAgentByGraphId< + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + params?: GetV2GetLibraryAgentByGraphIdParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV2GetLibraryAgentByGraphIdQueryOptions( + graphId, + params, + options, + ); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * Get Library Agent from Store Listing Version ID. + * @summary Get Agent By Store ID + */ +export type getV2GetAgentByStoreIdResponse200 = { + data: GetV2GetAgentByStoreId200; + status: 200; +}; + +export type getV2GetAgentByStoreIdResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type getV2GetAgentByStoreIdResponseComposite = + | getV2GetAgentByStoreIdResponse200 + | getV2GetAgentByStoreIdResponse422; + +export type getV2GetAgentByStoreIdResponse = + getV2GetAgentByStoreIdResponseComposite & { + headers: Headers; + }; + +export const getGetV2GetAgentByStoreIdUrl = (storeListingVersionId: string) => { + return `/api/library/agents/marketplace/${storeListingVersionId}`; +}; + +export const getV2GetAgentByStoreId = async ( + storeListingVersionId: string, + options?: RequestInit, +): Promise => { + return customMutator( + getGetV2GetAgentByStoreIdUrl(storeListingVersionId), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV2GetAgentByStoreIdQueryKey = ( + storeListingVersionId: string, +) => { + return [`/api/library/agents/marketplace/${storeListingVersionId}`] as const; +}; + +export const getGetV2GetAgentByStoreIdQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + storeListingVersionId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? + getGetV2GetAgentByStoreIdQueryKey(storeListingVersionId); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV2GetAgentByStoreId(storeListingVersionId, { + signal, + ...requestOptions, + }); + + return { + queryKey, + queryFn, + enabled: !!storeListingVersionId, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV2GetAgentByStoreIdQueryResult = NonNullable< + Awaited> +>; +export type GetV2GetAgentByStoreIdQueryError = HTTPValidationError; + +export function useGetV2GetAgentByStoreId< + TData = Awaited>, + TError = HTTPValidationError, +>( + storeListingVersionId: string, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2GetAgentByStoreId< + TData = Awaited>, + TError = HTTPValidationError, +>( + storeListingVersionId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2GetAgentByStoreId< + TData = Awaited>, + TError = HTTPValidationError, +>( + storeListingVersionId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Get Agent By Store ID + */ + +export function useGetV2GetAgentByStoreId< + TData = Awaited>, + TError = HTTPValidationError, +>( + storeListingVersionId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV2GetAgentByStoreIdQueryOptions( + storeListingVersionId, + options, + ); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Fork Library Agent + */ +export type postV2ForkLibraryAgentResponse200 = { + data: LibraryAgent; + status: 200; +}; + +export type postV2ForkLibraryAgentResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV2ForkLibraryAgentResponseComposite = + | postV2ForkLibraryAgentResponse200 + | postV2ForkLibraryAgentResponse422; + +export type postV2ForkLibraryAgentResponse = + postV2ForkLibraryAgentResponseComposite & { + headers: Headers; + }; + +export const getPostV2ForkLibraryAgentUrl = (libraryAgentId: string) => { + return `/api/library/agents/${libraryAgentId}/fork`; +}; + +export const postV2ForkLibraryAgent = async ( + libraryAgentId: string, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV2ForkLibraryAgentUrl(libraryAgentId), + { + ...options, + method: "POST", + }, + ); +}; + +export const getPostV2ForkLibraryAgentMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { libraryAgentId: string }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { libraryAgentId: string }, + TContext +> => { + const mutationKey = ["postV2ForkLibraryAgent"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { libraryAgentId: string } + > = (props) => { + const { libraryAgentId } = props ?? {}; + + return postV2ForkLibraryAgent(libraryAgentId, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV2ForkLibraryAgentMutationResult = NonNullable< + Awaited> +>; + +export type PostV2ForkLibraryAgentMutationError = HTTPValidationError; + +/** + * @summary Fork Library Agent + */ +export const usePostV2ForkLibraryAgent = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { libraryAgentId: string }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { libraryAgentId: string }, + TContext +> => { + const mutationOptions = getPostV2ForkLibraryAgentMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * Sets up a webhook-triggered `LibraryAgentPreset` for a `LibraryAgent`. +Returns the correspondingly created `LibraryAgentPreset` with `webhook_id` set. + * @summary Setup Trigger + */ +export type postV2SetupTriggerResponse200 = { + data: LibraryAgentPreset; + status: 200; +}; + +export type postV2SetupTriggerResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV2SetupTriggerResponseComposite = + | postV2SetupTriggerResponse200 + | postV2SetupTriggerResponse422; + +export type postV2SetupTriggerResponse = postV2SetupTriggerResponseComposite & { + headers: Headers; +}; + +export const getPostV2SetupTriggerUrl = (libraryAgentId: string) => { + return `/api/library/agents/${libraryAgentId}/setup-trigger`; +}; + +export const postV2SetupTrigger = async ( + libraryAgentId: string, + triggeredPresetSetupParams: TriggeredPresetSetupParams, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV2SetupTriggerUrl(libraryAgentId), + { + ...options, + method: "POST", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(triggeredPresetSetupParams), + }, + ); +}; + +export const getPostV2SetupTriggerMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { libraryAgentId: string; data: TriggeredPresetSetupParams }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { libraryAgentId: string; data: TriggeredPresetSetupParams }, + TContext +> => { + const mutationKey = ["postV2SetupTrigger"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { libraryAgentId: string; data: TriggeredPresetSetupParams } + > = (props) => { + const { libraryAgentId, data } = props ?? {}; + + return postV2SetupTrigger(libraryAgentId, data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV2SetupTriggerMutationResult = NonNullable< + Awaited> +>; +export type PostV2SetupTriggerMutationBody = TriggeredPresetSetupParams; +export type PostV2SetupTriggerMutationError = HTTPValidationError; + +/** + * @summary Setup Trigger + */ +export const usePostV2SetupTrigger = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { libraryAgentId: string; data: TriggeredPresetSetupParams }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { libraryAgentId: string; data: TriggeredPresetSetupParams }, + TContext +> => { + const mutationOptions = getPostV2SetupTriggerMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/endpoints/onboarding/onboarding.ts b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/onboarding/onboarding.ts new file mode 100644 index 0000000000..74b107a15a --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/onboarding/onboarding.ts @@ -0,0 +1,666 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import { useMutation, useQuery } from "@tanstack/react-query"; +import type { + DataTag, + DefinedInitialDataOptions, + DefinedUseQueryResult, + MutationFunction, + QueryClient, + QueryFunction, + QueryKey, + UndefinedInitialDataOptions, + UseMutationOptions, + UseMutationResult, + UseQueryOptions, + UseQueryResult, +} from "@tanstack/react-query"; + +import type { HTTPValidationError } from "../../models/hTTPValidationError"; + +import type { UserOnboardingUpdate } from "../../models/userOnboardingUpdate"; + +import { customMutator } from "../../../mutators/custom-mutator"; + +type SecondParameter unknown> = Parameters[1]; + +/** + * @summary Get onboarding status + */ +export type getV1GetOnboardingStatusResponse200 = { + data: unknown; + status: 200; +}; + +export type getV1GetOnboardingStatusResponseComposite = + getV1GetOnboardingStatusResponse200; + +export type getV1GetOnboardingStatusResponse = + getV1GetOnboardingStatusResponseComposite & { + headers: Headers; + }; + +export const getGetV1GetOnboardingStatusUrl = () => { + return `/api/onboarding`; +}; + +export const getV1GetOnboardingStatus = async ( + options?: RequestInit, +): Promise => { + return customMutator( + getGetV1GetOnboardingStatusUrl(), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV1GetOnboardingStatusQueryKey = () => { + return [`/api/onboarding`] as const; +}; + +export const getGetV1GetOnboardingStatusQueryOptions = < + TData = Awaited>, + TError = unknown, +>(options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; +}) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetV1GetOnboardingStatusQueryKey(); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => getV1GetOnboardingStatus({ signal, ...requestOptions }); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV1GetOnboardingStatusQueryResult = NonNullable< + Awaited> +>; +export type GetV1GetOnboardingStatusQueryError = unknown; + +export function useGetV1GetOnboardingStatus< + TData = Awaited>, + TError = unknown, +>( + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetOnboardingStatus< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetOnboardingStatus< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Get onboarding status + */ + +export function useGetV1GetOnboardingStatus< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV1GetOnboardingStatusQueryOptions(options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Update onboarding progress + */ +export type patchV1UpdateOnboardingProgressResponse200 = { + data: unknown; + status: 200; +}; + +export type patchV1UpdateOnboardingProgressResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type patchV1UpdateOnboardingProgressResponseComposite = + | patchV1UpdateOnboardingProgressResponse200 + | patchV1UpdateOnboardingProgressResponse422; + +export type patchV1UpdateOnboardingProgressResponse = + patchV1UpdateOnboardingProgressResponseComposite & { + headers: Headers; + }; + +export const getPatchV1UpdateOnboardingProgressUrl = () => { + return `/api/onboarding`; +}; + +export const patchV1UpdateOnboardingProgress = async ( + userOnboardingUpdate: UserOnboardingUpdate, + options?: RequestInit, +): Promise => { + return customMutator( + getPatchV1UpdateOnboardingProgressUrl(), + { + ...options, + method: "PATCH", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(userOnboardingUpdate), + }, + ); +}; + +export const getPatchV1UpdateOnboardingProgressMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: UserOnboardingUpdate }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { data: UserOnboardingUpdate }, + TContext +> => { + const mutationKey = ["patchV1UpdateOnboardingProgress"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { data: UserOnboardingUpdate } + > = (props) => { + const { data } = props ?? {}; + + return patchV1UpdateOnboardingProgress(data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PatchV1UpdateOnboardingProgressMutationResult = NonNullable< + Awaited> +>; +export type PatchV1UpdateOnboardingProgressMutationBody = UserOnboardingUpdate; +export type PatchV1UpdateOnboardingProgressMutationError = HTTPValidationError; + +/** + * @summary Update onboarding progress + */ +export const usePatchV1UpdateOnboardingProgress = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: UserOnboardingUpdate }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { data: UserOnboardingUpdate }, + TContext +> => { + const mutationOptions = + getPatchV1UpdateOnboardingProgressMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * @summary Get recommended agents + */ +export type getV1GetRecommendedAgentsResponse200 = { + data: unknown; + status: 200; +}; + +export type getV1GetRecommendedAgentsResponseComposite = + getV1GetRecommendedAgentsResponse200; + +export type getV1GetRecommendedAgentsResponse = + getV1GetRecommendedAgentsResponseComposite & { + headers: Headers; + }; + +export const getGetV1GetRecommendedAgentsUrl = () => { + return `/api/onboarding/agents`; +}; + +export const getV1GetRecommendedAgents = async ( + options?: RequestInit, +): Promise => { + return customMutator( + getGetV1GetRecommendedAgentsUrl(), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV1GetRecommendedAgentsQueryKey = () => { + return [`/api/onboarding/agents`] as const; +}; + +export const getGetV1GetRecommendedAgentsQueryOptions = < + TData = Awaited>, + TError = unknown, +>(options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; +}) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetV1GetRecommendedAgentsQueryKey(); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => getV1GetRecommendedAgents({ signal, ...requestOptions }); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV1GetRecommendedAgentsQueryResult = NonNullable< + Awaited> +>; +export type GetV1GetRecommendedAgentsQueryError = unknown; + +export function useGetV1GetRecommendedAgents< + TData = Awaited>, + TError = unknown, +>( + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetRecommendedAgents< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1GetRecommendedAgents< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Get recommended agents + */ + +export function useGetV1GetRecommendedAgents< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV1GetRecommendedAgentsQueryOptions(options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Check onboarding enabled + */ +export type getV1CheckOnboardingEnabledResponse200 = { + data: unknown; + status: 200; +}; + +export type getV1CheckOnboardingEnabledResponseComposite = + getV1CheckOnboardingEnabledResponse200; + +export type getV1CheckOnboardingEnabledResponse = + getV1CheckOnboardingEnabledResponseComposite & { + headers: Headers; + }; + +export const getGetV1CheckOnboardingEnabledUrl = () => { + return `/api/onboarding/enabled`; +}; + +export const getV1CheckOnboardingEnabled = async ( + options?: RequestInit, +): Promise => { + return customMutator( + getGetV1CheckOnboardingEnabledUrl(), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV1CheckOnboardingEnabledQueryKey = () => { + return [`/api/onboarding/enabled`] as const; +}; + +export const getGetV1CheckOnboardingEnabledQueryOptions = < + TData = Awaited>, + TError = unknown, +>(options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; +}) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetV1CheckOnboardingEnabledQueryKey(); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV1CheckOnboardingEnabled({ signal, ...requestOptions }); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV1CheckOnboardingEnabledQueryResult = NonNullable< + Awaited> +>; +export type GetV1CheckOnboardingEnabledQueryError = unknown; + +export function useGetV1CheckOnboardingEnabled< + TData = Awaited>, + TError = unknown, +>( + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1CheckOnboardingEnabled< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1CheckOnboardingEnabled< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Check onboarding enabled + */ + +export function useGetV1CheckOnboardingEnabled< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV1CheckOnboardingEnabledQueryOptions(options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/endpoints/otto/otto.ts b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/otto/otto.ts new file mode 100644 index 0000000000..9096ff2be8 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/otto/otto.ts @@ -0,0 +1,139 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import { useMutation } from "@tanstack/react-query"; +import type { + MutationFunction, + QueryClient, + UseMutationOptions, + UseMutationResult, +} from "@tanstack/react-query"; + +import type { ApiResponse } from "../../models/apiResponse"; + +import type { ChatRequest } from "../../models/chatRequest"; + +import type { HTTPValidationError } from "../../models/hTTPValidationError"; + +import { customMutator } from "../../../mutators/custom-mutator"; + +type SecondParameter unknown> = Parameters[1]; + +/** + * Proxy requests to Otto API while adding necessary security headers and logging. +Requires an authenticated user. + * @summary Proxy Otto Chat Request + */ +export type postV2ProxyOttoChatRequestResponse200 = { + data: ApiResponse; + status: 200; +}; + +export type postV2ProxyOttoChatRequestResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV2ProxyOttoChatRequestResponseComposite = + | postV2ProxyOttoChatRequestResponse200 + | postV2ProxyOttoChatRequestResponse422; + +export type postV2ProxyOttoChatRequestResponse = + postV2ProxyOttoChatRequestResponseComposite & { + headers: Headers; + }; + +export const getPostV2ProxyOttoChatRequestUrl = () => { + return `/api/otto/ask`; +}; + +export const postV2ProxyOttoChatRequest = async ( + chatRequest: ChatRequest, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV2ProxyOttoChatRequestUrl(), + { + ...options, + method: "POST", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(chatRequest), + }, + ); +}; + +export const getPostV2ProxyOttoChatRequestMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: ChatRequest }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { data: ChatRequest }, + TContext +> => { + const mutationKey = ["postV2ProxyOttoChatRequest"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { data: ChatRequest } + > = (props) => { + const { data } = props ?? {}; + + return postV2ProxyOttoChatRequest(data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV2ProxyOttoChatRequestMutationResult = NonNullable< + Awaited> +>; +export type PostV2ProxyOttoChatRequestMutationBody = ChatRequest; +export type PostV2ProxyOttoChatRequestMutationError = HTTPValidationError; + +/** + * @summary Proxy Otto Chat Request + */ +export const usePostV2ProxyOttoChatRequest = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: ChatRequest }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { data: ChatRequest }, + TContext +> => { + const mutationOptions = getPostV2ProxyOttoChatRequestMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/endpoints/presets/presets.ts b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/presets/presets.ts new file mode 100644 index 0000000000..1fade88cb8 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/presets/presets.ts @@ -0,0 +1,895 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import { useMutation, useQuery } from "@tanstack/react-query"; +import type { + DataTag, + DefinedInitialDataOptions, + DefinedUseQueryResult, + MutationFunction, + QueryClient, + QueryFunction, + QueryKey, + UndefinedInitialDataOptions, + UseMutationOptions, + UseMutationResult, + UseQueryOptions, + UseQueryResult, +} from "@tanstack/react-query"; + +import type { BodyPostV2ExecuteAPreset } from "../../models/bodyPostV2ExecuteAPreset"; + +import type { GetV2ListPresetsParams } from "../../models/getV2ListPresetsParams"; + +import type { HTTPValidationError } from "../../models/hTTPValidationError"; + +import type { LibraryAgentPreset } from "../../models/libraryAgentPreset"; + +import type { LibraryAgentPresetResponse } from "../../models/libraryAgentPresetResponse"; + +import type { LibraryAgentPresetUpdatable } from "../../models/libraryAgentPresetUpdatable"; + +import type { PostV2CreateANewPresetBody } from "../../models/postV2CreateANewPresetBody"; + +import type { PostV2ExecuteAPreset200 } from "../../models/postV2ExecuteAPreset200"; + +import { customMutator } from "../../../mutators/custom-mutator"; + +type SecondParameter unknown> = Parameters[1]; + +/** + * Retrieve a paginated list of presets for the current user. + * @summary List presets + */ +export type getV2ListPresetsResponse200 = { + data: LibraryAgentPresetResponse; + status: 200; +}; + +export type getV2ListPresetsResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type getV2ListPresetsResponseComposite = + | getV2ListPresetsResponse200 + | getV2ListPresetsResponse422; + +export type getV2ListPresetsResponse = getV2ListPresetsResponseComposite & { + headers: Headers; +}; + +export const getGetV2ListPresetsUrl = (params: GetV2ListPresetsParams) => { + const normalizedParams = new URLSearchParams(); + + Object.entries(params || {}).forEach(([key, value]) => { + if (value !== undefined) { + normalizedParams.append(key, value === null ? "null" : value.toString()); + } + }); + + const stringifiedParams = normalizedParams.toString(); + + return stringifiedParams.length > 0 + ? `/api/library/presets?${stringifiedParams}` + : `/api/library/presets`; +}; + +export const getV2ListPresets = async ( + params: GetV2ListPresetsParams, + options?: RequestInit, +): Promise => { + return customMutator( + getGetV2ListPresetsUrl(params), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV2ListPresetsQueryKey = (params: GetV2ListPresetsParams) => { + return [`/api/library/presets`, ...(params ? [params] : [])] as const; +}; + +export const getGetV2ListPresetsQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + params: GetV2ListPresetsParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetV2ListPresetsQueryKey(params); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => getV2ListPresets(params, { signal, ...requestOptions }); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV2ListPresetsQueryResult = NonNullable< + Awaited> +>; +export type GetV2ListPresetsQueryError = HTTPValidationError; + +export function useGetV2ListPresets< + TData = Awaited>, + TError = HTTPValidationError, +>( + params: GetV2ListPresetsParams, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2ListPresets< + TData = Awaited>, + TError = HTTPValidationError, +>( + params: GetV2ListPresetsParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2ListPresets< + TData = Awaited>, + TError = HTTPValidationError, +>( + params: GetV2ListPresetsParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary List presets + */ + +export function useGetV2ListPresets< + TData = Awaited>, + TError = HTTPValidationError, +>( + params: GetV2ListPresetsParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV2ListPresetsQueryOptions(params, options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * Create a new preset for the current user. + * @summary Create a new preset + */ +export type postV2CreateANewPresetResponse200 = { + data: LibraryAgentPreset; + status: 200; +}; + +export type postV2CreateANewPresetResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV2CreateANewPresetResponseComposite = + | postV2CreateANewPresetResponse200 + | postV2CreateANewPresetResponse422; + +export type postV2CreateANewPresetResponse = + postV2CreateANewPresetResponseComposite & { + headers: Headers; + }; + +export const getPostV2CreateANewPresetUrl = () => { + return `/api/library/presets`; +}; + +export const postV2CreateANewPreset = async ( + postV2CreateANewPresetBody: PostV2CreateANewPresetBody, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV2CreateANewPresetUrl(), + { + ...options, + method: "POST", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(postV2CreateANewPresetBody), + }, + ); +}; + +export const getPostV2CreateANewPresetMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: PostV2CreateANewPresetBody }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { data: PostV2CreateANewPresetBody }, + TContext +> => { + const mutationKey = ["postV2CreateANewPreset"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { data: PostV2CreateANewPresetBody } + > = (props) => { + const { data } = props ?? {}; + + return postV2CreateANewPreset(data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV2CreateANewPresetMutationResult = NonNullable< + Awaited> +>; +export type PostV2CreateANewPresetMutationBody = PostV2CreateANewPresetBody; +export type PostV2CreateANewPresetMutationError = HTTPValidationError; + +/** + * @summary Create a new preset + */ +export const usePostV2CreateANewPreset = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: PostV2CreateANewPresetBody }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { data: PostV2CreateANewPresetBody }, + TContext +> => { + const mutationOptions = getPostV2CreateANewPresetMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * Retrieve details for a specific preset by its ID. + * @summary Get a specific preset + */ +export type getV2GetASpecificPresetResponse200 = { + data: LibraryAgentPreset; + status: 200; +}; + +export type getV2GetASpecificPresetResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type getV2GetASpecificPresetResponseComposite = + | getV2GetASpecificPresetResponse200 + | getV2GetASpecificPresetResponse422; + +export type getV2GetASpecificPresetResponse = + getV2GetASpecificPresetResponseComposite & { + headers: Headers; + }; + +export const getGetV2GetASpecificPresetUrl = (presetId: string) => { + return `/api/library/presets/${presetId}`; +}; + +export const getV2GetASpecificPreset = async ( + presetId: string, + options?: RequestInit, +): Promise => { + return customMutator( + getGetV2GetASpecificPresetUrl(presetId), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV2GetASpecificPresetQueryKey = (presetId: string) => { + return [`/api/library/presets/${presetId}`] as const; +}; + +export const getGetV2GetASpecificPresetQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + presetId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetV2GetASpecificPresetQueryKey(presetId); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV2GetASpecificPreset(presetId, { signal, ...requestOptions }); + + return { + queryKey, + queryFn, + enabled: !!presetId, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV2GetASpecificPresetQueryResult = NonNullable< + Awaited> +>; +export type GetV2GetASpecificPresetQueryError = HTTPValidationError; + +export function useGetV2GetASpecificPreset< + TData = Awaited>, + TError = HTTPValidationError, +>( + presetId: string, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2GetASpecificPreset< + TData = Awaited>, + TError = HTTPValidationError, +>( + presetId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2GetASpecificPreset< + TData = Awaited>, + TError = HTTPValidationError, +>( + presetId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Get a specific preset + */ + +export function useGetV2GetASpecificPreset< + TData = Awaited>, + TError = HTTPValidationError, +>( + presetId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV2GetASpecificPresetQueryOptions( + presetId, + options, + ); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * Update an existing preset by its ID. + * @summary Update an existing preset + */ +export type patchV2UpdateAnExistingPresetResponse200 = { + data: LibraryAgentPreset; + status: 200; +}; + +export type patchV2UpdateAnExistingPresetResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type patchV2UpdateAnExistingPresetResponseComposite = + | patchV2UpdateAnExistingPresetResponse200 + | patchV2UpdateAnExistingPresetResponse422; + +export type patchV2UpdateAnExistingPresetResponse = + patchV2UpdateAnExistingPresetResponseComposite & { + headers: Headers; + }; + +export const getPatchV2UpdateAnExistingPresetUrl = (presetId: string) => { + return `/api/library/presets/${presetId}`; +}; + +export const patchV2UpdateAnExistingPreset = async ( + presetId: string, + libraryAgentPresetUpdatable: LibraryAgentPresetUpdatable, + options?: RequestInit, +): Promise => { + return customMutator( + getPatchV2UpdateAnExistingPresetUrl(presetId), + { + ...options, + method: "PATCH", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(libraryAgentPresetUpdatable), + }, + ); +}; + +export const getPatchV2UpdateAnExistingPresetMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { presetId: string; data: LibraryAgentPresetUpdatable }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { presetId: string; data: LibraryAgentPresetUpdatable }, + TContext +> => { + const mutationKey = ["patchV2UpdateAnExistingPreset"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { presetId: string; data: LibraryAgentPresetUpdatable } + > = (props) => { + const { presetId, data } = props ?? {}; + + return patchV2UpdateAnExistingPreset(presetId, data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PatchV2UpdateAnExistingPresetMutationResult = NonNullable< + Awaited> +>; +export type PatchV2UpdateAnExistingPresetMutationBody = + LibraryAgentPresetUpdatable; +export type PatchV2UpdateAnExistingPresetMutationError = HTTPValidationError; + +/** + * @summary Update an existing preset + */ +export const usePatchV2UpdateAnExistingPreset = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { presetId: string; data: LibraryAgentPresetUpdatable }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { presetId: string; data: LibraryAgentPresetUpdatable }, + TContext +> => { + const mutationOptions = + getPatchV2UpdateAnExistingPresetMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * Delete an existing preset by its ID. + * @summary Delete a preset + */ +export type deleteV2DeleteAPresetResponse204 = { + data: void; + status: 204; +}; + +export type deleteV2DeleteAPresetResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type deleteV2DeleteAPresetResponseComposite = + | deleteV2DeleteAPresetResponse204 + | deleteV2DeleteAPresetResponse422; + +export type deleteV2DeleteAPresetResponse = + deleteV2DeleteAPresetResponseComposite & { + headers: Headers; + }; + +export const getDeleteV2DeleteAPresetUrl = (presetId: string) => { + return `/api/library/presets/${presetId}`; +}; + +export const deleteV2DeleteAPreset = async ( + presetId: string, + options?: RequestInit, +): Promise => { + return customMutator( + getDeleteV2DeleteAPresetUrl(presetId), + { + ...options, + method: "DELETE", + }, + ); +}; + +export const getDeleteV2DeleteAPresetMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { presetId: string }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { presetId: string }, + TContext +> => { + const mutationKey = ["deleteV2DeleteAPreset"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { presetId: string } + > = (props) => { + const { presetId } = props ?? {}; + + return deleteV2DeleteAPreset(presetId, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type DeleteV2DeleteAPresetMutationResult = NonNullable< + Awaited> +>; + +export type DeleteV2DeleteAPresetMutationError = HTTPValidationError; + +/** + * @summary Delete a preset + */ +export const useDeleteV2DeleteAPreset = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { presetId: string }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { presetId: string }, + TContext +> => { + const mutationOptions = getDeleteV2DeleteAPresetMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * Execute a preset with the given graph and node input for the current user. + * @summary Execute a preset + */ +export type postV2ExecuteAPresetResponse200 = { + data: PostV2ExecuteAPreset200; + status: 200; +}; + +export type postV2ExecuteAPresetResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV2ExecuteAPresetResponseComposite = + | postV2ExecuteAPresetResponse200 + | postV2ExecuteAPresetResponse422; + +export type postV2ExecuteAPresetResponse = + postV2ExecuteAPresetResponseComposite & { + headers: Headers; + }; + +export const getPostV2ExecuteAPresetUrl = (presetId: string) => { + return `/api/library/presets/${presetId}/execute`; +}; + +export const postV2ExecuteAPreset = async ( + presetId: string, + bodyPostV2ExecuteAPreset: BodyPostV2ExecuteAPreset, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV2ExecuteAPresetUrl(presetId), + { + ...options, + method: "POST", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(bodyPostV2ExecuteAPreset), + }, + ); +}; + +export const getPostV2ExecuteAPresetMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { presetId: string; data: BodyPostV2ExecuteAPreset }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { presetId: string; data: BodyPostV2ExecuteAPreset }, + TContext +> => { + const mutationKey = ["postV2ExecuteAPreset"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { presetId: string; data: BodyPostV2ExecuteAPreset } + > = (props) => { + const { presetId, data } = props ?? {}; + + return postV2ExecuteAPreset(presetId, data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV2ExecuteAPresetMutationResult = NonNullable< + Awaited> +>; +export type PostV2ExecuteAPresetMutationBody = BodyPostV2ExecuteAPreset; +export type PostV2ExecuteAPresetMutationError = HTTPValidationError; + +/** + * @summary Execute a preset + */ +export const usePostV2ExecuteAPreset = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { presetId: string; data: BodyPostV2ExecuteAPreset }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { presetId: string; data: BodyPostV2ExecuteAPreset }, + TContext +> => { + const mutationOptions = getPostV2ExecuteAPresetMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/endpoints/schedules/schedules.ts b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/schedules/schedules.ts new file mode 100644 index 0000000000..6d0e9f655a --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/schedules/schedules.ts @@ -0,0 +1,640 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import { useMutation, useQuery } from "@tanstack/react-query"; +import type { + DataTag, + DefinedInitialDataOptions, + DefinedUseQueryResult, + MutationFunction, + QueryClient, + QueryFunction, + QueryKey, + UndefinedInitialDataOptions, + UseMutationOptions, + UseMutationResult, + UseQueryOptions, + UseQueryResult, +} from "@tanstack/react-query"; + +import type { DeleteV1DeleteExecutionSchedule200 } from "../../models/deleteV1DeleteExecutionSchedule200"; + +import type { GraphExecutionJobInfo } from "../../models/graphExecutionJobInfo"; + +import type { HTTPValidationError } from "../../models/hTTPValidationError"; + +import type { ScheduleCreationRequest } from "../../models/scheduleCreationRequest"; + +import { customMutator } from "../../../mutators/custom-mutator"; + +type SecondParameter unknown> = Parameters[1]; + +/** + * @summary Create execution schedule + */ +export type postV1CreateExecutionScheduleResponse200 = { + data: GraphExecutionJobInfo; + status: 200; +}; + +export type postV1CreateExecutionScheduleResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV1CreateExecutionScheduleResponseComposite = + | postV1CreateExecutionScheduleResponse200 + | postV1CreateExecutionScheduleResponse422; + +export type postV1CreateExecutionScheduleResponse = + postV1CreateExecutionScheduleResponseComposite & { + headers: Headers; + }; + +export const getPostV1CreateExecutionScheduleUrl = (graphId: string) => { + return `/api/graphs/${graphId}/schedules`; +}; + +export const postV1CreateExecutionSchedule = async ( + graphId: string, + scheduleCreationRequest: ScheduleCreationRequest, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV1CreateExecutionScheduleUrl(graphId), + { + ...options, + method: "POST", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(scheduleCreationRequest), + }, + ); +}; + +export const getPostV1CreateExecutionScheduleMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { graphId: string; data: ScheduleCreationRequest }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { graphId: string; data: ScheduleCreationRequest }, + TContext +> => { + const mutationKey = ["postV1CreateExecutionSchedule"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { graphId: string; data: ScheduleCreationRequest } + > = (props) => { + const { graphId, data } = props ?? {}; + + return postV1CreateExecutionSchedule(graphId, data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV1CreateExecutionScheduleMutationResult = NonNullable< + Awaited> +>; +export type PostV1CreateExecutionScheduleMutationBody = ScheduleCreationRequest; +export type PostV1CreateExecutionScheduleMutationError = HTTPValidationError; + +/** + * @summary Create execution schedule + */ +export const usePostV1CreateExecutionSchedule = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { graphId: string; data: ScheduleCreationRequest }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { graphId: string; data: ScheduleCreationRequest }, + TContext +> => { + const mutationOptions = + getPostV1CreateExecutionScheduleMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * @summary List execution schedules for a graph + */ +export type getV1ListExecutionSchedulesForAGraphResponse200 = { + data: GraphExecutionJobInfo[]; + status: 200; +}; + +export type getV1ListExecutionSchedulesForAGraphResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type getV1ListExecutionSchedulesForAGraphResponseComposite = + | getV1ListExecutionSchedulesForAGraphResponse200 + | getV1ListExecutionSchedulesForAGraphResponse422; + +export type getV1ListExecutionSchedulesForAGraphResponse = + getV1ListExecutionSchedulesForAGraphResponseComposite & { + headers: Headers; + }; + +export const getGetV1ListExecutionSchedulesForAGraphUrl = (graphId: string) => { + return `/api/graphs/${graphId}/schedules`; +}; + +export const getV1ListExecutionSchedulesForAGraph = async ( + graphId: string, + options?: RequestInit, +): Promise => { + return customMutator( + getGetV1ListExecutionSchedulesForAGraphUrl(graphId), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV1ListExecutionSchedulesForAGraphQueryKey = ( + graphId: string, +) => { + return [`/api/graphs/${graphId}/schedules`] as const; +}; + +export const getGetV1ListExecutionSchedulesForAGraphQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? + getGetV1ListExecutionSchedulesForAGraphQueryKey(graphId); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV1ListExecutionSchedulesForAGraph(graphId, { + signal, + ...requestOptions, + }); + + return { + queryKey, + queryFn, + enabled: !!graphId, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV1ListExecutionSchedulesForAGraphQueryResult = NonNullable< + Awaited> +>; +export type GetV1ListExecutionSchedulesForAGraphQueryError = + HTTPValidationError; + +export function useGetV1ListExecutionSchedulesForAGraph< + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1ListExecutionSchedulesForAGraph< + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1ListExecutionSchedulesForAGraph< + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary List execution schedules for a graph + */ + +export function useGetV1ListExecutionSchedulesForAGraph< + TData = Awaited>, + TError = HTTPValidationError, +>( + graphId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV1ListExecutionSchedulesForAGraphQueryOptions( + graphId, + options, + ); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary List execution schedules for a user + */ +export type getV1ListExecutionSchedulesForAUserResponse200 = { + data: GraphExecutionJobInfo[]; + status: 200; +}; + +export type getV1ListExecutionSchedulesForAUserResponseComposite = + getV1ListExecutionSchedulesForAUserResponse200; + +export type getV1ListExecutionSchedulesForAUserResponse = + getV1ListExecutionSchedulesForAUserResponseComposite & { + headers: Headers; + }; + +export const getGetV1ListExecutionSchedulesForAUserUrl = () => { + return `/api/schedules`; +}; + +export const getV1ListExecutionSchedulesForAUser = async ( + options?: RequestInit, +): Promise => { + return customMutator( + getGetV1ListExecutionSchedulesForAUserUrl(), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV1ListExecutionSchedulesForAUserQueryKey = () => { + return [`/api/schedules`] as const; +}; + +export const getGetV1ListExecutionSchedulesForAUserQueryOptions = < + TData = Awaited>, + TError = unknown, +>(options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; +}) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetV1ListExecutionSchedulesForAUserQueryKey(); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV1ListExecutionSchedulesForAUser({ signal, ...requestOptions }); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV1ListExecutionSchedulesForAUserQueryResult = NonNullable< + Awaited> +>; +export type GetV1ListExecutionSchedulesForAUserQueryError = unknown; + +export function useGetV1ListExecutionSchedulesForAUser< + TData = Awaited>, + TError = unknown, +>( + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1ListExecutionSchedulesForAUser< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV1ListExecutionSchedulesForAUser< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary List execution schedules for a user + */ + +export function useGetV1ListExecutionSchedulesForAUser< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = + getGetV1ListExecutionSchedulesForAUserQueryOptions(options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Delete execution schedule + */ +export type deleteV1DeleteExecutionScheduleResponse200 = { + data: DeleteV1DeleteExecutionSchedule200; + status: 200; +}; + +export type deleteV1DeleteExecutionScheduleResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type deleteV1DeleteExecutionScheduleResponseComposite = + | deleteV1DeleteExecutionScheduleResponse200 + | deleteV1DeleteExecutionScheduleResponse422; + +export type deleteV1DeleteExecutionScheduleResponse = + deleteV1DeleteExecutionScheduleResponseComposite & { + headers: Headers; + }; + +export const getDeleteV1DeleteExecutionScheduleUrl = (scheduleId: string) => { + return `/api/schedules/${scheduleId}`; +}; + +export const deleteV1DeleteExecutionSchedule = async ( + scheduleId: string, + options?: RequestInit, +): Promise => { + return customMutator( + getDeleteV1DeleteExecutionScheduleUrl(scheduleId), + { + ...options, + method: "DELETE", + }, + ); +}; + +export const getDeleteV1DeleteExecutionScheduleMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { scheduleId: string }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { scheduleId: string }, + TContext +> => { + const mutationKey = ["deleteV1DeleteExecutionSchedule"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { scheduleId: string } + > = (props) => { + const { scheduleId } = props ?? {}; + + return deleteV1DeleteExecutionSchedule(scheduleId, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type DeleteV1DeleteExecutionScheduleMutationResult = NonNullable< + Awaited> +>; + +export type DeleteV1DeleteExecutionScheduleMutationError = HTTPValidationError; + +/** + * @summary Delete execution schedule + */ +export const useDeleteV1DeleteExecutionSchedule = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { scheduleId: string }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { scheduleId: string }, + TContext +> => { + const mutationOptions = + getDeleteV1DeleteExecutionScheduleMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/endpoints/store/store.ts b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/store/store.ts new file mode 100644 index 0000000000..5970371244 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/store/store.ts @@ -0,0 +1,2841 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import { useMutation, useQuery } from "@tanstack/react-query"; +import type { + DataTag, + DefinedInitialDataOptions, + DefinedUseQueryResult, + MutationFunction, + QueryClient, + QueryFunction, + QueryKey, + UndefinedInitialDataOptions, + UseMutationOptions, + UseMutationResult, + UseQueryOptions, + UseQueryResult, +} from "@tanstack/react-query"; + +import type { BodyPostV2UploadSubmissionMedia } from "../../models/bodyPostV2UploadSubmissionMedia"; + +import type { CreatorDetails } from "../../models/creatorDetails"; + +import type { CreatorsResponse } from "../../models/creatorsResponse"; + +import type { GetV2ListMySubmissionsParams } from "../../models/getV2ListMySubmissionsParams"; + +import type { GetV2ListStoreAgentsParams } from "../../models/getV2ListStoreAgentsParams"; + +import type { GetV2ListStoreCreatorsParams } from "../../models/getV2ListStoreCreatorsParams"; + +import type { HTTPValidationError } from "../../models/hTTPValidationError"; + +import type { MyAgentsResponse } from "../../models/myAgentsResponse"; + +import type { PostV2GenerateSubmissionImageParams } from "../../models/postV2GenerateSubmissionImageParams"; + +import type { Profile } from "../../models/profile"; + +import type { ProfileDetails } from "../../models/profileDetails"; + +import type { StoreAgentDetails } from "../../models/storeAgentDetails"; + +import type { StoreAgentsResponse } from "../../models/storeAgentsResponse"; + +import type { StoreReview } from "../../models/storeReview"; + +import type { StoreReviewCreate } from "../../models/storeReviewCreate"; + +import type { StoreSubmission } from "../../models/storeSubmission"; + +import type { StoreSubmissionRequest } from "../../models/storeSubmissionRequest"; + +import type { StoreSubmissionsResponse } from "../../models/storeSubmissionsResponse"; + +import { customMutator } from "../../../mutators/custom-mutator"; + +type SecondParameter unknown> = Parameters[1]; + +/** + * Get the profile details for the authenticated user. + * @summary Get user profile + */ +export type getV2GetUserProfileResponse200 = { + data: ProfileDetails; + status: 200; +}; + +export type getV2GetUserProfileResponseComposite = + getV2GetUserProfileResponse200; + +export type getV2GetUserProfileResponse = + getV2GetUserProfileResponseComposite & { + headers: Headers; + }; + +export const getGetV2GetUserProfileUrl = () => { + return `/api/store/profile`; +}; + +export const getV2GetUserProfile = async ( + options?: RequestInit, +): Promise => { + return customMutator( + getGetV2GetUserProfileUrl(), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV2GetUserProfileQueryKey = () => { + return [`/api/store/profile`] as const; +}; + +export const getGetV2GetUserProfileQueryOptions = < + TData = Awaited>, + TError = unknown, +>(options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; +}) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getGetV2GetUserProfileQueryKey(); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => getV2GetUserProfile({ signal, ...requestOptions }); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV2GetUserProfileQueryResult = NonNullable< + Awaited> +>; +export type GetV2GetUserProfileQueryError = unknown; + +export function useGetV2GetUserProfile< + TData = Awaited>, + TError = unknown, +>( + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2GetUserProfile< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2GetUserProfile< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Get user profile + */ + +export function useGetV2GetUserProfile< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV2GetUserProfileQueryOptions(options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * Update the store profile for the authenticated user. + +Args: + profile (Profile): The updated profile details + user_id (str): ID of the authenticated user + +Returns: + CreatorDetails: The updated profile + +Raises: + HTTPException: If there is an error updating the profile + * @summary Update user profile + */ +export type postV2UpdateUserProfileResponse200 = { + data: CreatorDetails; + status: 200; +}; + +export type postV2UpdateUserProfileResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV2UpdateUserProfileResponseComposite = + | postV2UpdateUserProfileResponse200 + | postV2UpdateUserProfileResponse422; + +export type postV2UpdateUserProfileResponse = + postV2UpdateUserProfileResponseComposite & { + headers: Headers; + }; + +export const getPostV2UpdateUserProfileUrl = () => { + return `/api/store/profile`; +}; + +export const postV2UpdateUserProfile = async ( + profile: Profile, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV2UpdateUserProfileUrl(), + { + ...options, + method: "POST", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(profile), + }, + ); +}; + +export const getPostV2UpdateUserProfileMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: Profile }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { data: Profile }, + TContext +> => { + const mutationKey = ["postV2UpdateUserProfile"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { data: Profile } + > = (props) => { + const { data } = props ?? {}; + + return postV2UpdateUserProfile(data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV2UpdateUserProfileMutationResult = NonNullable< + Awaited> +>; +export type PostV2UpdateUserProfileMutationBody = Profile; +export type PostV2UpdateUserProfileMutationError = HTTPValidationError; + +/** + * @summary Update user profile + */ +export const usePostV2UpdateUserProfile = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: Profile }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { data: Profile }, + TContext +> => { + const mutationOptions = getPostV2UpdateUserProfileMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * Get a paginated list of agents from the store with optional filtering and sorting. + +Args: + featured (bool, optional): Filter to only show featured agents. Defaults to False. + creator (str | None, optional): Filter agents by creator username. Defaults to None. + sorted_by (str | None, optional): Sort agents by "runs" or "rating". Defaults to None. + search_query (str | None, optional): Search agents by name, subheading and description. Defaults to None. + category (str | None, optional): Filter agents by category. Defaults to None. + page (int, optional): Page number for pagination. Defaults to 1. + page_size (int, optional): Number of agents per page. Defaults to 20. + +Returns: + StoreAgentsResponse: Paginated list of agents matching the filters + +Raises: + HTTPException: If page or page_size are less than 1 + +Used for: +- Home Page Featured Agents +- Home Page Top Agents +- Search Results +- Agent Details - Other Agents By Creator +- Agent Details - Similar Agents +- Creator Details - Agents By Creator + * @summary List store agents + */ +export type getV2ListStoreAgentsResponse200 = { + data: StoreAgentsResponse; + status: 200; +}; + +export type getV2ListStoreAgentsResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type getV2ListStoreAgentsResponseComposite = + | getV2ListStoreAgentsResponse200 + | getV2ListStoreAgentsResponse422; + +export type getV2ListStoreAgentsResponse = + getV2ListStoreAgentsResponseComposite & { + headers: Headers; + }; + +export const getGetV2ListStoreAgentsUrl = ( + params?: GetV2ListStoreAgentsParams, +) => { + const normalizedParams = new URLSearchParams(); + + Object.entries(params || {}).forEach(([key, value]) => { + if (value !== undefined) { + normalizedParams.append(key, value === null ? "null" : value.toString()); + } + }); + + const stringifiedParams = normalizedParams.toString(); + + return stringifiedParams.length > 0 + ? `/api/store/agents?${stringifiedParams}` + : `/api/store/agents`; +}; + +export const getV2ListStoreAgents = async ( + params?: GetV2ListStoreAgentsParams, + options?: RequestInit, +): Promise => { + return customMutator( + getGetV2ListStoreAgentsUrl(params), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV2ListStoreAgentsQueryKey = ( + params?: GetV2ListStoreAgentsParams, +) => { + return [`/api/store/agents`, ...(params ? [params] : [])] as const; +}; + +export const getGetV2ListStoreAgentsQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: GetV2ListStoreAgentsParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetV2ListStoreAgentsQueryKey(params); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV2ListStoreAgents(params, { signal, ...requestOptions }); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV2ListStoreAgentsQueryResult = NonNullable< + Awaited> +>; +export type GetV2ListStoreAgentsQueryError = HTTPValidationError; + +export function useGetV2ListStoreAgents< + TData = Awaited>, + TError = HTTPValidationError, +>( + params: undefined | GetV2ListStoreAgentsParams, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2ListStoreAgents< + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: GetV2ListStoreAgentsParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2ListStoreAgents< + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: GetV2ListStoreAgentsParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary List store agents + */ + +export function useGetV2ListStoreAgents< + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: GetV2ListStoreAgentsParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV2ListStoreAgentsQueryOptions(params, options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * This is only used on the AgentDetails Page + +It returns the store listing agents details. + * @summary Get specific agent + */ +export type getV2GetSpecificAgentResponse200 = { + data: StoreAgentDetails; + status: 200; +}; + +export type getV2GetSpecificAgentResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type getV2GetSpecificAgentResponseComposite = + | getV2GetSpecificAgentResponse200 + | getV2GetSpecificAgentResponse422; + +export type getV2GetSpecificAgentResponse = + getV2GetSpecificAgentResponseComposite & { + headers: Headers; + }; + +export const getGetV2GetSpecificAgentUrl = ( + username: string, + agentName: string, +) => { + return `/api/store/agents/${username}/${agentName}`; +}; + +export const getV2GetSpecificAgent = async ( + username: string, + agentName: string, + options?: RequestInit, +): Promise => { + return customMutator( + getGetV2GetSpecificAgentUrl(username, agentName), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV2GetSpecificAgentQueryKey = ( + username: string, + agentName: string, +) => { + return [`/api/store/agents/${username}/${agentName}`] as const; +}; + +export const getGetV2GetSpecificAgentQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + username: string, + agentName: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? + getGetV2GetSpecificAgentQueryKey(username, agentName); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV2GetSpecificAgent(username, agentName, { signal, ...requestOptions }); + + return { + queryKey, + queryFn, + enabled: !!(username && agentName), + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV2GetSpecificAgentQueryResult = NonNullable< + Awaited> +>; +export type GetV2GetSpecificAgentQueryError = HTTPValidationError; + +export function useGetV2GetSpecificAgent< + TData = Awaited>, + TError = HTTPValidationError, +>( + username: string, + agentName: string, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2GetSpecificAgent< + TData = Awaited>, + TError = HTTPValidationError, +>( + username: string, + agentName: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2GetSpecificAgent< + TData = Awaited>, + TError = HTTPValidationError, +>( + username: string, + agentName: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Get specific agent + */ + +export function useGetV2GetSpecificAgent< + TData = Awaited>, + TError = HTTPValidationError, +>( + username: string, + agentName: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV2GetSpecificAgentQueryOptions( + username, + agentName, + options, + ); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * Get Agent Graph from Store Listing Version ID. + * @summary Get agent graph + */ +export type getV2GetAgentGraphResponse200 = { + data: unknown; + status: 200; +}; + +export type getV2GetAgentGraphResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type getV2GetAgentGraphResponseComposite = + | getV2GetAgentGraphResponse200 + | getV2GetAgentGraphResponse422; + +export type getV2GetAgentGraphResponse = getV2GetAgentGraphResponseComposite & { + headers: Headers; +}; + +export const getGetV2GetAgentGraphUrl = (storeListingVersionId: string) => { + return `/api/store/graph/${storeListingVersionId}`; +}; + +export const getV2GetAgentGraph = async ( + storeListingVersionId: string, + options?: RequestInit, +): Promise => { + return customMutator( + getGetV2GetAgentGraphUrl(storeListingVersionId), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV2GetAgentGraphQueryKey = ( + storeListingVersionId: string, +) => { + return [`/api/store/graph/${storeListingVersionId}`] as const; +}; + +export const getGetV2GetAgentGraphQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + storeListingVersionId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? + getGetV2GetAgentGraphQueryKey(storeListingVersionId); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV2GetAgentGraph(storeListingVersionId, { signal, ...requestOptions }); + + return { + queryKey, + queryFn, + enabled: !!storeListingVersionId, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV2GetAgentGraphQueryResult = NonNullable< + Awaited> +>; +export type GetV2GetAgentGraphQueryError = HTTPValidationError; + +export function useGetV2GetAgentGraph< + TData = Awaited>, + TError = HTTPValidationError, +>( + storeListingVersionId: string, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2GetAgentGraph< + TData = Awaited>, + TError = HTTPValidationError, +>( + storeListingVersionId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2GetAgentGraph< + TData = Awaited>, + TError = HTTPValidationError, +>( + storeListingVersionId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Get agent graph + */ + +export function useGetV2GetAgentGraph< + TData = Awaited>, + TError = HTTPValidationError, +>( + storeListingVersionId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV2GetAgentGraphQueryOptions( + storeListingVersionId, + options, + ); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * Get Store Agent Details from Store Listing Version ID. + * @summary Get agent by version + */ +export type getV2GetAgentByVersionResponse200 = { + data: StoreAgentDetails; + status: 200; +}; + +export type getV2GetAgentByVersionResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type getV2GetAgentByVersionResponseComposite = + | getV2GetAgentByVersionResponse200 + | getV2GetAgentByVersionResponse422; + +export type getV2GetAgentByVersionResponse = + getV2GetAgentByVersionResponseComposite & { + headers: Headers; + }; + +export const getGetV2GetAgentByVersionUrl = (storeListingVersionId: string) => { + return `/api/store/agents/${storeListingVersionId}`; +}; + +export const getV2GetAgentByVersion = async ( + storeListingVersionId: string, + options?: RequestInit, +): Promise => { + return customMutator( + getGetV2GetAgentByVersionUrl(storeListingVersionId), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV2GetAgentByVersionQueryKey = ( + storeListingVersionId: string, +) => { + return [`/api/store/agents/${storeListingVersionId}`] as const; +}; + +export const getGetV2GetAgentByVersionQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + storeListingVersionId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? + getGetV2GetAgentByVersionQueryKey(storeListingVersionId); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV2GetAgentByVersion(storeListingVersionId, { + signal, + ...requestOptions, + }); + + return { + queryKey, + queryFn, + enabled: !!storeListingVersionId, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV2GetAgentByVersionQueryResult = NonNullable< + Awaited> +>; +export type GetV2GetAgentByVersionQueryError = HTTPValidationError; + +export function useGetV2GetAgentByVersion< + TData = Awaited>, + TError = HTTPValidationError, +>( + storeListingVersionId: string, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2GetAgentByVersion< + TData = Awaited>, + TError = HTTPValidationError, +>( + storeListingVersionId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2GetAgentByVersion< + TData = Awaited>, + TError = HTTPValidationError, +>( + storeListingVersionId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Get agent by version + */ + +export function useGetV2GetAgentByVersion< + TData = Awaited>, + TError = HTTPValidationError, +>( + storeListingVersionId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV2GetAgentByVersionQueryOptions( + storeListingVersionId, + options, + ); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * Create a review for a store agent. + +Args: + username: Creator's username + agent_name: Name/slug of the agent + review: Review details including score and optional comments + user_id: ID of authenticated user creating the review + +Returns: + The created review + * @summary Create agent review + */ +export type postV2CreateAgentReviewResponse200 = { + data: StoreReview; + status: 200; +}; + +export type postV2CreateAgentReviewResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV2CreateAgentReviewResponseComposite = + | postV2CreateAgentReviewResponse200 + | postV2CreateAgentReviewResponse422; + +export type postV2CreateAgentReviewResponse = + postV2CreateAgentReviewResponseComposite & { + headers: Headers; + }; + +export const getPostV2CreateAgentReviewUrl = ( + username: string, + agentName: string, +) => { + return `/api/store/agents/${username}/${agentName}/review`; +}; + +export const postV2CreateAgentReview = async ( + username: string, + agentName: string, + storeReviewCreate: StoreReviewCreate, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV2CreateAgentReviewUrl(username, agentName), + { + ...options, + method: "POST", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(storeReviewCreate), + }, + ); +}; + +export const getPostV2CreateAgentReviewMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { username: string; agentName: string; data: StoreReviewCreate }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { username: string; agentName: string; data: StoreReviewCreate }, + TContext +> => { + const mutationKey = ["postV2CreateAgentReview"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { username: string; agentName: string; data: StoreReviewCreate } + > = (props) => { + const { username, agentName, data } = props ?? {}; + + return postV2CreateAgentReview(username, agentName, data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV2CreateAgentReviewMutationResult = NonNullable< + Awaited> +>; +export type PostV2CreateAgentReviewMutationBody = StoreReviewCreate; +export type PostV2CreateAgentReviewMutationError = HTTPValidationError; + +/** + * @summary Create agent review + */ +export const usePostV2CreateAgentReview = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { username: string; agentName: string; data: StoreReviewCreate }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { username: string; agentName: string; data: StoreReviewCreate }, + TContext +> => { + const mutationOptions = getPostV2CreateAgentReviewMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * This is needed for: +- Home Page Featured Creators +- Search Results Page + +--- + +To support this functionality we need: +- featured: bool - to limit the list to just featured agents +- search_query: str - vector search based on the creators profile description. +- sorted_by: [agent_rating, agent_runs] - + * @summary List store creators + */ +export type getV2ListStoreCreatorsResponse200 = { + data: CreatorsResponse; + status: 200; +}; + +export type getV2ListStoreCreatorsResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type getV2ListStoreCreatorsResponseComposite = + | getV2ListStoreCreatorsResponse200 + | getV2ListStoreCreatorsResponse422; + +export type getV2ListStoreCreatorsResponse = + getV2ListStoreCreatorsResponseComposite & { + headers: Headers; + }; + +export const getGetV2ListStoreCreatorsUrl = ( + params?: GetV2ListStoreCreatorsParams, +) => { + const normalizedParams = new URLSearchParams(); + + Object.entries(params || {}).forEach(([key, value]) => { + if (value !== undefined) { + normalizedParams.append(key, value === null ? "null" : value.toString()); + } + }); + + const stringifiedParams = normalizedParams.toString(); + + return stringifiedParams.length > 0 + ? `/api/store/creators?${stringifiedParams}` + : `/api/store/creators`; +}; + +export const getV2ListStoreCreators = async ( + params?: GetV2ListStoreCreatorsParams, + options?: RequestInit, +): Promise => { + return customMutator( + getGetV2ListStoreCreatorsUrl(params), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV2ListStoreCreatorsQueryKey = ( + params?: GetV2ListStoreCreatorsParams, +) => { + return [`/api/store/creators`, ...(params ? [params] : [])] as const; +}; + +export const getGetV2ListStoreCreatorsQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: GetV2ListStoreCreatorsParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetV2ListStoreCreatorsQueryKey(params); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV2ListStoreCreators(params, { signal, ...requestOptions }); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV2ListStoreCreatorsQueryResult = NonNullable< + Awaited> +>; +export type GetV2ListStoreCreatorsQueryError = HTTPValidationError; + +export function useGetV2ListStoreCreators< + TData = Awaited>, + TError = HTTPValidationError, +>( + params: undefined | GetV2ListStoreCreatorsParams, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2ListStoreCreators< + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: GetV2ListStoreCreatorsParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2ListStoreCreators< + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: GetV2ListStoreCreatorsParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary List store creators + */ + +export function useGetV2ListStoreCreators< + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: GetV2ListStoreCreatorsParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV2ListStoreCreatorsQueryOptions(params, options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * Get the details of a creator +- Creator Details Page + * @summary Get creator details + */ +export type getV2GetCreatorDetailsResponse200 = { + data: CreatorDetails; + status: 200; +}; + +export type getV2GetCreatorDetailsResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type getV2GetCreatorDetailsResponseComposite = + | getV2GetCreatorDetailsResponse200 + | getV2GetCreatorDetailsResponse422; + +export type getV2GetCreatorDetailsResponse = + getV2GetCreatorDetailsResponseComposite & { + headers: Headers; + }; + +export const getGetV2GetCreatorDetailsUrl = (username: string) => { + return `/api/store/creator/${username}`; +}; + +export const getV2GetCreatorDetails = async ( + username: string, + options?: RequestInit, +): Promise => { + return customMutator( + getGetV2GetCreatorDetailsUrl(username), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV2GetCreatorDetailsQueryKey = (username: string) => { + return [`/api/store/creator/${username}`] as const; +}; + +export const getGetV2GetCreatorDetailsQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + username: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetV2GetCreatorDetailsQueryKey(username); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV2GetCreatorDetails(username, { signal, ...requestOptions }); + + return { + queryKey, + queryFn, + enabled: !!username, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV2GetCreatorDetailsQueryResult = NonNullable< + Awaited> +>; +export type GetV2GetCreatorDetailsQueryError = HTTPValidationError; + +export function useGetV2GetCreatorDetails< + TData = Awaited>, + TError = HTTPValidationError, +>( + username: string, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2GetCreatorDetails< + TData = Awaited>, + TError = HTTPValidationError, +>( + username: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2GetCreatorDetails< + TData = Awaited>, + TError = HTTPValidationError, +>( + username: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Get creator details + */ + +export function useGetV2GetCreatorDetails< + TData = Awaited>, + TError = HTTPValidationError, +>( + username: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV2GetCreatorDetailsQueryOptions(username, options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * @summary Get my agents + */ +export type getV2GetMyAgentsResponse200 = { + data: MyAgentsResponse; + status: 200; +}; + +export type getV2GetMyAgentsResponseComposite = getV2GetMyAgentsResponse200; + +export type getV2GetMyAgentsResponse = getV2GetMyAgentsResponseComposite & { + headers: Headers; +}; + +export const getGetV2GetMyAgentsUrl = () => { + return `/api/store/myagents`; +}; + +export const getV2GetMyAgents = async ( + options?: RequestInit, +): Promise => { + return customMutator(getGetV2GetMyAgentsUrl(), { + ...options, + method: "GET", + }); +}; + +export const getGetV2GetMyAgentsQueryKey = () => { + return [`/api/store/myagents`] as const; +}; + +export const getGetV2GetMyAgentsQueryOptions = < + TData = Awaited>, + TError = unknown, +>(options?: { + query?: Partial< + UseQueryOptions>, TError, TData> + >; + request?: SecondParameter; +}) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = queryOptions?.queryKey ?? getGetV2GetMyAgentsQueryKey(); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => getV2GetMyAgents({ signal, ...requestOptions }); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV2GetMyAgentsQueryResult = NonNullable< + Awaited> +>; +export type GetV2GetMyAgentsQueryError = unknown; + +export function useGetV2GetMyAgents< + TData = Awaited>, + TError = unknown, +>( + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2GetMyAgents< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2GetMyAgents< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Get my agents + */ + +export function useGetV2GetMyAgents< + TData = Awaited>, + TError = unknown, +>( + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV2GetMyAgentsQueryOptions(options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * Delete a store listing submission. + +Args: + user_id (str): ID of the authenticated user + submission_id (str): ID of the submission to be deleted + +Returns: + bool: True if the submission was successfully deleted, False otherwise + * @summary Delete store submission + */ +export type deleteV2DeleteStoreSubmissionResponse200 = { + data: boolean; + status: 200; +}; + +export type deleteV2DeleteStoreSubmissionResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type deleteV2DeleteStoreSubmissionResponseComposite = + | deleteV2DeleteStoreSubmissionResponse200 + | deleteV2DeleteStoreSubmissionResponse422; + +export type deleteV2DeleteStoreSubmissionResponse = + deleteV2DeleteStoreSubmissionResponseComposite & { + headers: Headers; + }; + +export const getDeleteV2DeleteStoreSubmissionUrl = (submissionId: string) => { + return `/api/store/submissions/${submissionId}`; +}; + +export const deleteV2DeleteStoreSubmission = async ( + submissionId: string, + options?: RequestInit, +): Promise => { + return customMutator( + getDeleteV2DeleteStoreSubmissionUrl(submissionId), + { + ...options, + method: "DELETE", + }, + ); +}; + +export const getDeleteV2DeleteStoreSubmissionMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { submissionId: string }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { submissionId: string }, + TContext +> => { + const mutationKey = ["deleteV2DeleteStoreSubmission"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { submissionId: string } + > = (props) => { + const { submissionId } = props ?? {}; + + return deleteV2DeleteStoreSubmission(submissionId, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type DeleteV2DeleteStoreSubmissionMutationResult = NonNullable< + Awaited> +>; + +export type DeleteV2DeleteStoreSubmissionMutationError = HTTPValidationError; + +/** + * @summary Delete store submission + */ +export const useDeleteV2DeleteStoreSubmission = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { submissionId: string }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { submissionId: string }, + TContext +> => { + const mutationOptions = + getDeleteV2DeleteStoreSubmissionMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * Get a paginated list of store submissions for the authenticated user. + +Args: + user_id (str): ID of the authenticated user + page (int, optional): Page number for pagination. Defaults to 1. + page_size (int, optional): Number of submissions per page. Defaults to 20. + +Returns: + StoreListingsResponse: Paginated list of store submissions + +Raises: + HTTPException: If page or page_size are less than 1 + * @summary List my submissions + */ +export type getV2ListMySubmissionsResponse200 = { + data: StoreSubmissionsResponse; + status: 200; +}; + +export type getV2ListMySubmissionsResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type getV2ListMySubmissionsResponseComposite = + | getV2ListMySubmissionsResponse200 + | getV2ListMySubmissionsResponse422; + +export type getV2ListMySubmissionsResponse = + getV2ListMySubmissionsResponseComposite & { + headers: Headers; + }; + +export const getGetV2ListMySubmissionsUrl = ( + params?: GetV2ListMySubmissionsParams, +) => { + const normalizedParams = new URLSearchParams(); + + Object.entries(params || {}).forEach(([key, value]) => { + if (value !== undefined) { + normalizedParams.append(key, value === null ? "null" : value.toString()); + } + }); + + const stringifiedParams = normalizedParams.toString(); + + return stringifiedParams.length > 0 + ? `/api/store/submissions?${stringifiedParams}` + : `/api/store/submissions`; +}; + +export const getV2ListMySubmissions = async ( + params?: GetV2ListMySubmissionsParams, + options?: RequestInit, +): Promise => { + return customMutator( + getGetV2ListMySubmissionsUrl(params), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV2ListMySubmissionsQueryKey = ( + params?: GetV2ListMySubmissionsParams, +) => { + return [`/api/store/submissions`, ...(params ? [params] : [])] as const; +}; + +export const getGetV2ListMySubmissionsQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: GetV2ListMySubmissionsParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? getGetV2ListMySubmissionsQueryKey(params); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV2ListMySubmissions(params, { signal, ...requestOptions }); + + return { queryKey, queryFn, ...queryOptions } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV2ListMySubmissionsQueryResult = NonNullable< + Awaited> +>; +export type GetV2ListMySubmissionsQueryError = HTTPValidationError; + +export function useGetV2ListMySubmissions< + TData = Awaited>, + TError = HTTPValidationError, +>( + params: undefined | GetV2ListMySubmissionsParams, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2ListMySubmissions< + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: GetV2ListMySubmissionsParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2ListMySubmissions< + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: GetV2ListMySubmissionsParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary List my submissions + */ + +export function useGetV2ListMySubmissions< + TData = Awaited>, + TError = HTTPValidationError, +>( + params?: GetV2ListMySubmissionsParams, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV2ListMySubmissionsQueryOptions(params, options); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} + +/** + * Create a new store listing submission. + +Args: + submission_request (StoreSubmissionRequest): The submission details + user_id (str): ID of the authenticated user submitting the listing + +Returns: + StoreSubmission: The created store submission + +Raises: + HTTPException: If there is an error creating the submission + * @summary Create store submission + */ +export type postV2CreateStoreSubmissionResponse200 = { + data: StoreSubmission; + status: 200; +}; + +export type postV2CreateStoreSubmissionResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV2CreateStoreSubmissionResponseComposite = + | postV2CreateStoreSubmissionResponse200 + | postV2CreateStoreSubmissionResponse422; + +export type postV2CreateStoreSubmissionResponse = + postV2CreateStoreSubmissionResponseComposite & { + headers: Headers; + }; + +export const getPostV2CreateStoreSubmissionUrl = () => { + return `/api/store/submissions`; +}; + +export const postV2CreateStoreSubmission = async ( + storeSubmissionRequest: StoreSubmissionRequest, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV2CreateStoreSubmissionUrl(), + { + ...options, + method: "POST", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(storeSubmissionRequest), + }, + ); +}; + +export const getPostV2CreateStoreSubmissionMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: StoreSubmissionRequest }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { data: StoreSubmissionRequest }, + TContext +> => { + const mutationKey = ["postV2CreateStoreSubmission"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { data: StoreSubmissionRequest } + > = (props) => { + const { data } = props ?? {}; + + return postV2CreateStoreSubmission(data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV2CreateStoreSubmissionMutationResult = NonNullable< + Awaited> +>; +export type PostV2CreateStoreSubmissionMutationBody = StoreSubmissionRequest; +export type PostV2CreateStoreSubmissionMutationError = HTTPValidationError; + +/** + * @summary Create store submission + */ +export const usePostV2CreateStoreSubmission = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: StoreSubmissionRequest }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { data: StoreSubmissionRequest }, + TContext +> => { + const mutationOptions = + getPostV2CreateStoreSubmissionMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * Upload media (images/videos) for a store listing submission. + +Args: + file (UploadFile): The media file to upload + user_id (str): ID of the authenticated user uploading the media + +Returns: + str: URL of the uploaded media file + +Raises: + HTTPException: If there is an error uploading the media + * @summary Upload submission media + */ +export type postV2UploadSubmissionMediaResponse200 = { + data: unknown; + status: 200; +}; + +export type postV2UploadSubmissionMediaResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV2UploadSubmissionMediaResponseComposite = + | postV2UploadSubmissionMediaResponse200 + | postV2UploadSubmissionMediaResponse422; + +export type postV2UploadSubmissionMediaResponse = + postV2UploadSubmissionMediaResponseComposite & { + headers: Headers; + }; + +export const getPostV2UploadSubmissionMediaUrl = () => { + return `/api/store/submissions/media`; +}; + +export const postV2UploadSubmissionMedia = async ( + bodyPostV2UploadSubmissionMedia: BodyPostV2UploadSubmissionMedia, + options?: RequestInit, +): Promise => { + const formData = new FormData(); + formData.append(`file`, bodyPostV2UploadSubmissionMedia.file); + + return customMutator( + getPostV2UploadSubmissionMediaUrl(), + { + ...options, + method: "POST", + body: formData, + }, + ); +}; + +export const getPostV2UploadSubmissionMediaMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: BodyPostV2UploadSubmissionMedia }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { data: BodyPostV2UploadSubmissionMedia }, + TContext +> => { + const mutationKey = ["postV2UploadSubmissionMedia"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { data: BodyPostV2UploadSubmissionMedia } + > = (props) => { + const { data } = props ?? {}; + + return postV2UploadSubmissionMedia(data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV2UploadSubmissionMediaMutationResult = NonNullable< + Awaited> +>; +export type PostV2UploadSubmissionMediaMutationBody = + BodyPostV2UploadSubmissionMedia; +export type PostV2UploadSubmissionMediaMutationError = HTTPValidationError; + +/** + * @summary Upload submission media + */ +export const usePostV2UploadSubmissionMedia = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: BodyPostV2UploadSubmissionMedia }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { data: BodyPostV2UploadSubmissionMedia }, + TContext +> => { + const mutationOptions = + getPostV2UploadSubmissionMediaMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * Generate an image for a store listing submission. + +Args: + agent_id (str): ID of the agent to generate an image for + user_id (str): ID of the authenticated user + +Returns: + JSONResponse: JSON containing the URL of the generated image + * @summary Generate submission image + */ +export type postV2GenerateSubmissionImageResponse200 = { + data: unknown; + status: 200; +}; + +export type postV2GenerateSubmissionImageResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV2GenerateSubmissionImageResponseComposite = + | postV2GenerateSubmissionImageResponse200 + | postV2GenerateSubmissionImageResponse422; + +export type postV2GenerateSubmissionImageResponse = + postV2GenerateSubmissionImageResponseComposite & { + headers: Headers; + }; + +export const getPostV2GenerateSubmissionImageUrl = ( + params: PostV2GenerateSubmissionImageParams, +) => { + const normalizedParams = new URLSearchParams(); + + Object.entries(params || {}).forEach(([key, value]) => { + if (value !== undefined) { + normalizedParams.append(key, value === null ? "null" : value.toString()); + } + }); + + const stringifiedParams = normalizedParams.toString(); + + return stringifiedParams.length > 0 + ? `/api/store/submissions/generate_image?${stringifiedParams}` + : `/api/store/submissions/generate_image`; +}; + +export const postV2GenerateSubmissionImage = async ( + params: PostV2GenerateSubmissionImageParams, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV2GenerateSubmissionImageUrl(params), + { + ...options, + method: "POST", + }, + ); +}; + +export const getPostV2GenerateSubmissionImageMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { params: PostV2GenerateSubmissionImageParams }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { params: PostV2GenerateSubmissionImageParams }, + TContext +> => { + const mutationKey = ["postV2GenerateSubmissionImage"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { params: PostV2GenerateSubmissionImageParams } + > = (props) => { + const { params } = props ?? {}; + + return postV2GenerateSubmissionImage(params, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV2GenerateSubmissionImageMutationResult = NonNullable< + Awaited> +>; + +export type PostV2GenerateSubmissionImageMutationError = HTTPValidationError; + +/** + * @summary Generate submission image + */ +export const usePostV2GenerateSubmissionImage = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { params: PostV2GenerateSubmissionImageParams }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { params: PostV2GenerateSubmissionImageParams }, + TContext +> => { + const mutationOptions = + getPostV2GenerateSubmissionImageMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; +/** + * Download the agent file by streaming its content. + +Args: + store_listing_version_id (str): The ID of the agent to download + +Returns: + StreamingResponse: A streaming response containing the agent's graph data. + +Raises: + HTTPException: If the agent is not found or an unexpected error occurs. + * @summary Download agent file + */ +export type getV2DownloadAgentFileResponse200 = { + data: unknown; + status: 200; +}; + +export type getV2DownloadAgentFileResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type getV2DownloadAgentFileResponseComposite = + | getV2DownloadAgentFileResponse200 + | getV2DownloadAgentFileResponse422; + +export type getV2DownloadAgentFileResponse = + getV2DownloadAgentFileResponseComposite & { + headers: Headers; + }; + +export const getGetV2DownloadAgentFileUrl = (storeListingVersionId: string) => { + return `/api/store/download/agents/${storeListingVersionId}`; +}; + +export const getV2DownloadAgentFile = async ( + storeListingVersionId: string, + options?: RequestInit, +): Promise => { + return customMutator( + getGetV2DownloadAgentFileUrl(storeListingVersionId), + { + ...options, + method: "GET", + }, + ); +}; + +export const getGetV2DownloadAgentFileQueryKey = ( + storeListingVersionId: string, +) => { + return [`/api/store/download/agents/${storeListingVersionId}`] as const; +}; + +export const getGetV2DownloadAgentFileQueryOptions = < + TData = Awaited>, + TError = HTTPValidationError, +>( + storeListingVersionId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, +) => { + const { query: queryOptions, request: requestOptions } = options ?? {}; + + const queryKey = + queryOptions?.queryKey ?? + getGetV2DownloadAgentFileQueryKey(storeListingVersionId); + + const queryFn: QueryFunction< + Awaited> + > = ({ signal }) => + getV2DownloadAgentFile(storeListingVersionId, { + signal, + ...requestOptions, + }); + + return { + queryKey, + queryFn, + enabled: !!storeListingVersionId, + ...queryOptions, + } as UseQueryOptions< + Awaited>, + TError, + TData + > & { queryKey: DataTag }; +}; + +export type GetV2DownloadAgentFileQueryResult = NonNullable< + Awaited> +>; +export type GetV2DownloadAgentFileQueryError = HTTPValidationError; + +export function useGetV2DownloadAgentFile< + TData = Awaited>, + TError = HTTPValidationError, +>( + storeListingVersionId: string, + options: { + query: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + DefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): DefinedUseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2DownloadAgentFile< + TData = Awaited>, + TError = HTTPValidationError, +>( + storeListingVersionId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + > & + Pick< + UndefinedInitialDataOptions< + Awaited>, + TError, + Awaited> + >, + "initialData" + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +export function useGetV2DownloadAgentFile< + TData = Awaited>, + TError = HTTPValidationError, +>( + storeListingVersionId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +}; +/** + * @summary Download agent file + */ + +export function useGetV2DownloadAgentFile< + TData = Awaited>, + TError = HTTPValidationError, +>( + storeListingVersionId: string, + options?: { + query?: Partial< + UseQueryOptions< + Awaited>, + TError, + TData + > + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseQueryResult & { + queryKey: DataTag; +} { + const queryOptions = getGetV2DownloadAgentFileQueryOptions( + storeListingVersionId, + options, + ); + + const query = useQuery(queryOptions, queryClient) as UseQueryResult< + TData, + TError + > & { queryKey: DataTag }; + + query.queryKey = queryOptions.queryKey; + + return query; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/endpoints/turnstile/turnstile.ts b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/turnstile/turnstile.ts new file mode 100644 index 0000000000..6d9d937486 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/endpoints/turnstile/turnstile.ts @@ -0,0 +1,140 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import { useMutation } from "@tanstack/react-query"; +import type { + MutationFunction, + QueryClient, + UseMutationOptions, + UseMutationResult, +} from "@tanstack/react-query"; + +import type { HTTPValidationError } from "../../models/hTTPValidationError"; + +import type { TurnstileVerifyRequest } from "../../models/turnstileVerifyRequest"; + +import type { TurnstileVerifyResponse } from "../../models/turnstileVerifyResponse"; + +import { customMutator } from "../../../mutators/custom-mutator"; + +type SecondParameter unknown> = Parameters[1]; + +/** + * Verify a Cloudflare Turnstile token. +This endpoint verifies a token returned by the Cloudflare Turnstile challenge +on the client side. It returns whether the verification was successful. + * @summary Verify Turnstile Token + */ +export type postV2VerifyTurnstileTokenResponse200 = { + data: TurnstileVerifyResponse; + status: 200; +}; + +export type postV2VerifyTurnstileTokenResponse422 = { + data: HTTPValidationError; + status: 422; +}; + +export type postV2VerifyTurnstileTokenResponseComposite = + | postV2VerifyTurnstileTokenResponse200 + | postV2VerifyTurnstileTokenResponse422; + +export type postV2VerifyTurnstileTokenResponse = + postV2VerifyTurnstileTokenResponseComposite & { + headers: Headers; + }; + +export const getPostV2VerifyTurnstileTokenUrl = () => { + return `/api/turnstile/verify`; +}; + +export const postV2VerifyTurnstileToken = async ( + turnstileVerifyRequest: TurnstileVerifyRequest, + options?: RequestInit, +): Promise => { + return customMutator( + getPostV2VerifyTurnstileTokenUrl(), + { + ...options, + method: "POST", + headers: { "Content-Type": "application/json", ...options?.headers }, + body: JSON.stringify(turnstileVerifyRequest), + }, + ); +}; + +export const getPostV2VerifyTurnstileTokenMutationOptions = < + TError = HTTPValidationError, + TContext = unknown, +>(options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: TurnstileVerifyRequest }, + TContext + >; + request?: SecondParameter; +}): UseMutationOptions< + Awaited>, + TError, + { data: TurnstileVerifyRequest }, + TContext +> => { + const mutationKey = ["postV2VerifyTurnstileToken"]; + const { mutation: mutationOptions, request: requestOptions } = options + ? options.mutation && + "mutationKey" in options.mutation && + options.mutation.mutationKey + ? options + : { ...options, mutation: { ...options.mutation, mutationKey } } + : { mutation: { mutationKey }, request: undefined }; + + const mutationFn: MutationFunction< + Awaited>, + { data: TurnstileVerifyRequest } + > = (props) => { + const { data } = props ?? {}; + + return postV2VerifyTurnstileToken(data, requestOptions); + }; + + return { mutationFn, ...mutationOptions }; +}; + +export type PostV2VerifyTurnstileTokenMutationResult = NonNullable< + Awaited> +>; +export type PostV2VerifyTurnstileTokenMutationBody = TurnstileVerifyRequest; +export type PostV2VerifyTurnstileTokenMutationError = HTTPValidationError; + +/** + * @summary Verify Turnstile Token + */ +export const usePostV2VerifyTurnstileToken = < + TError = HTTPValidationError, + TContext = unknown, +>( + options?: { + mutation?: UseMutationOptions< + Awaited>, + TError, + { data: TurnstileVerifyRequest }, + TContext + >; + request?: SecondParameter; + }, + queryClient?: QueryClient, +): UseMutationResult< + Awaited>, + TError, + { data: TurnstileVerifyRequest }, + TContext +> => { + const mutationOptions = getPostV2VerifyTurnstileTokenMutationOptions(options); + + return useMutation(mutationOptions, queryClient); +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/aPIKeyCredentials.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/aPIKeyCredentials.ts new file mode 100644 index 0000000000..b91310a3ff --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/aPIKeyCredentials.ts @@ -0,0 +1,19 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { APIKeyCredentialsTitle } from "./aPIKeyCredentialsTitle"; +import type { APIKeyCredentialsExpiresAt } from "./aPIKeyCredentialsExpiresAt"; + +export interface APIKeyCredentials { + id?: string; + provider: string; + title?: APIKeyCredentialsTitle; + type?: "api_key"; + api_key: string; + /** Unix timestamp (seconds) indicating when the API key expires (if at all) */ + expires_at?: APIKeyCredentialsExpiresAt; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/aPIKeyCredentialsExpiresAt.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/aPIKeyCredentialsExpiresAt.ts new file mode 100644 index 0000000000..a33e4a5a0d --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/aPIKeyCredentialsExpiresAt.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +/** + * Unix timestamp (seconds) indicating when the API key expires (if at all) + */ +export type APIKeyCredentialsExpiresAt = number | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/aPIKeyCredentialsTitle.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/aPIKeyCredentialsTitle.ts new file mode 100644 index 0000000000..476b44abae --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/aPIKeyCredentialsTitle.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type APIKeyCredentialsTitle = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/aPIKeyPermission.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/aPIKeyPermission.ts new file mode 100644 index 0000000000..bae84cd1fd --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/aPIKeyPermission.ts @@ -0,0 +1,18 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type APIKeyPermission = + (typeof APIKeyPermission)[keyof typeof APIKeyPermission]; + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export const APIKeyPermission = { + EXECUTE_GRAPH: "EXECUTE_GRAPH", + READ_GRAPH: "READ_GRAPH", + EXECUTE_BLOCK: "EXECUTE_BLOCK", + READ_BLOCK: "READ_BLOCK", +} as const; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/aPIKeyStatus.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/aPIKeyStatus.ts new file mode 100644 index 0000000000..88dc84ae77 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/aPIKeyStatus.ts @@ -0,0 +1,16 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type APIKeyStatus = (typeof APIKeyStatus)[keyof typeof APIKeyStatus]; + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export const APIKeyStatus = { + ACTIVE: "ACTIVE", + REVOKED: "REVOKED", + SUSPENDED: "SUSPENDED", +} as const; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/aPIKeyWithoutHash.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/aPIKeyWithoutHash.ts new file mode 100644 index 0000000000..0165c66c18 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/aPIKeyWithoutHash.ts @@ -0,0 +1,26 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { APIKeyStatus } from "./aPIKeyStatus"; +import type { APIKeyPermission } from "./aPIKeyPermission"; +import type { APIKeyWithoutHashLastUsedAt } from "./aPIKeyWithoutHashLastUsedAt"; +import type { APIKeyWithoutHashRevokedAt } from "./aPIKeyWithoutHashRevokedAt"; +import type { APIKeyWithoutHashDescription } from "./aPIKeyWithoutHashDescription"; + +export interface APIKeyWithoutHash { + id: string; + name: string; + prefix: string; + postfix: string; + status: APIKeyStatus; + permissions: APIKeyPermission[]; + created_at: string; + last_used_at: APIKeyWithoutHashLastUsedAt; + revoked_at: APIKeyWithoutHashRevokedAt; + description: APIKeyWithoutHashDescription; + user_id: string; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/aPIKeyWithoutHashDescription.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/aPIKeyWithoutHashDescription.ts new file mode 100644 index 0000000000..46c9963d10 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/aPIKeyWithoutHashDescription.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type APIKeyWithoutHashDescription = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/aPIKeyWithoutHashLastUsedAt.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/aPIKeyWithoutHashLastUsedAt.ts new file mode 100644 index 0000000000..b8e1188d44 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/aPIKeyWithoutHashLastUsedAt.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type APIKeyWithoutHashLastUsedAt = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/aPIKeyWithoutHashRevokedAt.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/aPIKeyWithoutHashRevokedAt.ts new file mode 100644 index 0000000000..bd658f4624 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/aPIKeyWithoutHashRevokedAt.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type APIKeyWithoutHashRevokedAt = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/addUserCreditsResponse.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/addUserCreditsResponse.ts new file mode 100644 index 0000000000..7b5401117c --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/addUserCreditsResponse.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export interface AddUserCreditsResponse { + new_balance: number; + transaction_key: string; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/agentExecutionStatus.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/agentExecutionStatus.ts new file mode 100644 index 0000000000..59e5c6f655 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/agentExecutionStatus.ts @@ -0,0 +1,20 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type AgentExecutionStatus = + (typeof AgentExecutionStatus)[keyof typeof AgentExecutionStatus]; + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export const AgentExecutionStatus = { + INCOMPLETE: "INCOMPLETE", + QUEUED: "QUEUED", + RUNNING: "RUNNING", + COMPLETED: "COMPLETED", + TERMINATED: "TERMINATED", + FAILED: "FAILED", +} as const; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/apiResponse.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/apiResponse.ts new file mode 100644 index 0000000000..a238870205 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/apiResponse.ts @@ -0,0 +1,14 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { Document } from "./document"; + +export interface ApiResponse { + answer: string; + documents: Document[]; + success: boolean; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/autoTopUpConfig.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/autoTopUpConfig.ts new file mode 100644 index 0000000000..eb5e978069 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/autoTopUpConfig.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export interface AutoTopUpConfig { + amount: number; + threshold: number; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/baseGraphInput.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/baseGraphInput.ts new file mode 100644 index 0000000000..fa830948f6 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/baseGraphInput.ts @@ -0,0 +1,23 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { Node } from "./node"; +import type { Link } from "./link"; +import type { BaseGraphInputForkedFromId } from "./baseGraphInputForkedFromId"; +import type { BaseGraphInputForkedFromVersion } from "./baseGraphInputForkedFromVersion"; + +export interface BaseGraphInput { + id?: string; + version?: number; + is_active?: boolean; + name: string; + description: string; + nodes?: Node[]; + links?: Link[]; + forked_from_id?: BaseGraphInputForkedFromId; + forked_from_version?: BaseGraphInputForkedFromVersion; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/baseGraphInputForkedFromId.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/baseGraphInputForkedFromId.ts new file mode 100644 index 0000000000..a7636e58e2 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/baseGraphInputForkedFromId.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type BaseGraphInputForkedFromId = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/baseGraphInputForkedFromVersion.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/baseGraphInputForkedFromVersion.ts new file mode 100644 index 0000000000..65dc4e6004 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/baseGraphInputForkedFromVersion.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type BaseGraphInputForkedFromVersion = number | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/baseGraphOutput.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/baseGraphOutput.ts new file mode 100644 index 0000000000..22946f2918 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/baseGraphOutput.ts @@ -0,0 +1,27 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { Node } from "./node"; +import type { Link } from "./link"; +import type { BaseGraphOutputForkedFromId } from "./baseGraphOutputForkedFromId"; +import type { BaseGraphOutputForkedFromVersion } from "./baseGraphOutputForkedFromVersion"; +import type { BaseGraphOutputInputSchema } from "./baseGraphOutputInputSchema"; +import type { BaseGraphOutputOutputSchema } from "./baseGraphOutputOutputSchema"; + +export interface BaseGraphOutput { + id?: string; + version?: number; + is_active?: boolean; + name: string; + description: string; + nodes?: Node[]; + links?: Link[]; + forked_from_id?: BaseGraphOutputForkedFromId; + forked_from_version?: BaseGraphOutputForkedFromVersion; + readonly input_schema: BaseGraphOutputInputSchema; + readonly output_schema: BaseGraphOutputOutputSchema; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/baseGraphOutputForkedFromId.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/baseGraphOutputForkedFromId.ts new file mode 100644 index 0000000000..3775a99a5c --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/baseGraphOutputForkedFromId.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type BaseGraphOutputForkedFromId = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/baseGraphOutputForkedFromVersion.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/baseGraphOutputForkedFromVersion.ts new file mode 100644 index 0000000000..b2f46fca2a --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/baseGraphOutputForkedFromVersion.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type BaseGraphOutputForkedFromVersion = number | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/baseGraphOutputInputSchema.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/baseGraphOutputInputSchema.ts new file mode 100644 index 0000000000..8b64c5e137 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/baseGraphOutputInputSchema.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type BaseGraphOutputInputSchema = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/baseGraphOutputOutputSchema.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/baseGraphOutputOutputSchema.ts new file mode 100644 index 0000000000..ace1f96737 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/baseGraphOutputOutputSchema.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type BaseGraphOutputOutputSchema = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV1Callback.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV1Callback.ts new file mode 100644 index 0000000000..5d6db6f166 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV1Callback.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export interface BodyPostV1Callback { + code: string; + state_token: string; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV1ExecuteGraphAgent.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV1ExecuteGraphAgent.ts new file mode 100644 index 0000000000..1bab4130ae --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV1ExecuteGraphAgent.ts @@ -0,0 +1,14 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { BodyPostV1ExecuteGraphAgentInputs } from "./bodyPostV1ExecuteGraphAgentInputs"; +import type { BodyPostV1ExecuteGraphAgentCredentialsInputs } from "./bodyPostV1ExecuteGraphAgentCredentialsInputs"; + +export interface BodyPostV1ExecuteGraphAgent { + inputs?: BodyPostV1ExecuteGraphAgentInputs; + credentials_inputs?: BodyPostV1ExecuteGraphAgentCredentialsInputs; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV1ExecuteGraphAgentCredentialsInputs.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV1ExecuteGraphAgentCredentialsInputs.ts new file mode 100644 index 0000000000..6c37b20ef4 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV1ExecuteGraphAgentCredentialsInputs.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { CredentialsMetaInput } from "./credentialsMetaInput"; + +export type BodyPostV1ExecuteGraphAgentCredentialsInputs = { + [key: string]: CredentialsMetaInput; +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV1ExecuteGraphAgentInputs.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV1ExecuteGraphAgentInputs.ts new file mode 100644 index 0000000000..759c51fc1a --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV1ExecuteGraphAgentInputs.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type BodyPostV1ExecuteGraphAgentInputs = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV1LogRawAnalytics.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV1LogRawAnalytics.ts new file mode 100644 index 0000000000..92362203ae --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV1LogRawAnalytics.ts @@ -0,0 +1,16 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { BodyPostV1LogRawAnalyticsData } from "./bodyPostV1LogRawAnalyticsData"; + +export interface BodyPostV1LogRawAnalytics { + type: string; + /** The data to log */ + data: BodyPostV1LogRawAnalyticsData; + /** Indexable field for any count based analytical measures like page order clicking, tutorial step completion, etc. */ + data_index: string; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV1LogRawAnalyticsData.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV1LogRawAnalyticsData.ts new file mode 100644 index 0000000000..0ef9be6f56 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV1LogRawAnalyticsData.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +/** + * The data to log + */ +export type BodyPostV1LogRawAnalyticsData = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV2AddCreditsToUser.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV2AddCreditsToUser.ts new file mode 100644 index 0000000000..af7f628315 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV2AddCreditsToUser.ts @@ -0,0 +1,13 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export interface BodyPostV2AddCreditsToUser { + user_id: string; + amount: number; + comments: string; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV2AddMarketplaceAgent.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV2AddMarketplaceAgent.ts new file mode 100644 index 0000000000..154ae8047c --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV2AddMarketplaceAgent.ts @@ -0,0 +1,11 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export interface BodyPostV2AddMarketplaceAgent { + store_listing_version_id: string; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV2ExecuteAPreset.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV2ExecuteAPreset.ts new file mode 100644 index 0000000000..33041abadc --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV2ExecuteAPreset.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { BodyPostV2ExecuteAPresetInputs } from "./bodyPostV2ExecuteAPresetInputs"; + +export interface BodyPostV2ExecuteAPreset { + inputs?: BodyPostV2ExecuteAPresetInputs; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV2ExecuteAPresetInputs.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV2ExecuteAPresetInputs.ts new file mode 100644 index 0000000000..fbfdc61bf2 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV2ExecuteAPresetInputs.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type BodyPostV2ExecuteAPresetInputs = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV2UploadSubmissionMedia.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV2UploadSubmissionMedia.ts new file mode 100644 index 0000000000..0fe626c328 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/bodyPostV2UploadSubmissionMedia.ts @@ -0,0 +1,11 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export interface BodyPostV2UploadSubmissionMedia { + file: Blob; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/chatRequest.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/chatRequest.ts new file mode 100644 index 0000000000..b50b426282 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/chatRequest.ts @@ -0,0 +1,17 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { Message } from "./message"; +import type { ChatRequestGraphId } from "./chatRequestGraphId"; + +export interface ChatRequest { + query: string; + conversation_history: Message[]; + message_id: string; + include_graph_data?: boolean; + graph_id?: ChatRequestGraphId; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/chatRequestGraphId.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/chatRequestGraphId.ts new file mode 100644 index 0000000000..c5f583ee62 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/chatRequestGraphId.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type ChatRequestGraphId = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/createAPIKeyRequest.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/createAPIKeyRequest.ts new file mode 100644 index 0000000000..660f5023ab --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/createAPIKeyRequest.ts @@ -0,0 +1,15 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { APIKeyPermission } from "./aPIKeyPermission"; +import type { CreateAPIKeyRequestDescription } from "./createAPIKeyRequestDescription"; + +export interface CreateAPIKeyRequest { + name: string; + permissions: APIKeyPermission[]; + description?: CreateAPIKeyRequestDescription; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/createAPIKeyRequestDescription.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/createAPIKeyRequestDescription.ts new file mode 100644 index 0000000000..e284be9925 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/createAPIKeyRequestDescription.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type CreateAPIKeyRequestDescription = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/createAPIKeyResponse.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/createAPIKeyResponse.ts new file mode 100644 index 0000000000..bfd8cf62eb --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/createAPIKeyResponse.ts @@ -0,0 +1,13 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { APIKeyWithoutHash } from "./aPIKeyWithoutHash"; + +export interface CreateAPIKeyResponse { + api_key: APIKeyWithoutHash; + plain_text_key: string; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/createGraph.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/createGraph.ts new file mode 100644 index 0000000000..8548196cce --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/createGraph.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { Graph } from "./graph"; + +export interface CreateGraph { + graph: Graph; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/creator.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/creator.ts new file mode 100644 index 0000000000..78744ea400 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/creator.ts @@ -0,0 +1,18 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export interface Creator { + name: string; + username: string; + description: string; + avatar_url: string; + num_agents: number; + agent_rating: number; + agent_runs: number; + is_featured: boolean; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/creatorDetails.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/creatorDetails.ts new file mode 100644 index 0000000000..a28c983faa --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/creatorDetails.ts @@ -0,0 +1,18 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export interface CreatorDetails { + name: string; + username: string; + description: string; + links: string[]; + avatar_url: string; + agent_rating: number; + agent_runs: number; + top_categories: string[]; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/creatorsResponse.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/creatorsResponse.ts new file mode 100644 index 0000000000..315cbd1e34 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/creatorsResponse.ts @@ -0,0 +1,14 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { Creator } from "./creator"; +import type { Pagination } from "./pagination"; + +export interface CreatorsResponse { + creators: Creator[]; + pagination: Pagination; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsDeletionNeedsConfirmationResponse.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsDeletionNeedsConfirmationResponse.ts new file mode 100644 index 0000000000..6352fc9c39 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsDeletionNeedsConfirmationResponse.ts @@ -0,0 +1,13 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export interface CredentialsDeletionNeedsConfirmationResponse { + deleted?: false; + need_confirmation?: true; + message: string; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsDeletionResponse.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsDeletionResponse.ts new file mode 100644 index 0000000000..2c2a424aca --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsDeletionResponse.ts @@ -0,0 +1,14 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { CredentialsDeletionResponseRevoked } from "./credentialsDeletionResponseRevoked"; + +export interface CredentialsDeletionResponse { + deleted?: true; + /** Indicates whether the credentials were also revoked by their provider. `None`/`null` if not applicable, e.g. when deleting non-revocable credentials such as API keys. */ + revoked: CredentialsDeletionResponseRevoked; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsDeletionResponseRevoked.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsDeletionResponseRevoked.ts new file mode 100644 index 0000000000..08a8b79bd9 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsDeletionResponseRevoked.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +/** + * Indicates whether the credentials were also revoked by their provider. `None`/`null` if not applicable, e.g. when deleting non-revocable credentials such as API keys. + */ +export type CredentialsDeletionResponseRevoked = boolean | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsMetaInput.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsMetaInput.ts new file mode 100644 index 0000000000..bbff8a8227 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsMetaInput.ts @@ -0,0 +1,17 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { CredentialsMetaInputTitle } from "./credentialsMetaInputTitle"; +import type { ProviderName } from "./providerName"; +import type { CredentialsMetaInputType } from "./credentialsMetaInputType"; + +export interface CredentialsMetaInput { + id: string; + title?: CredentialsMetaInputTitle; + provider: ProviderName; + type: CredentialsMetaInputType; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsMetaInputTitle.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsMetaInputTitle.ts new file mode 100644 index 0000000000..1befe2af4e --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsMetaInputTitle.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type CredentialsMetaInputTitle = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsMetaInputType.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsMetaInputType.ts new file mode 100644 index 0000000000..ec13a6f1a5 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsMetaInputType.ts @@ -0,0 +1,18 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type CredentialsMetaInputType = + (typeof CredentialsMetaInputType)[keyof typeof CredentialsMetaInputType]; + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export const CredentialsMetaInputType = { + api_key: "api_key", + oauth2: "oauth2", + user_password: "user_password", + host_scoped: "host_scoped", +} as const; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsMetaResponse.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsMetaResponse.ts new file mode 100644 index 0000000000..bff9e1b95d --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsMetaResponse.ts @@ -0,0 +1,23 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { CredentialsMetaResponseType } from "./credentialsMetaResponseType"; +import type { CredentialsMetaResponseTitle } from "./credentialsMetaResponseTitle"; +import type { CredentialsMetaResponseScopes } from "./credentialsMetaResponseScopes"; +import type { CredentialsMetaResponseUsername } from "./credentialsMetaResponseUsername"; +import type { CredentialsMetaResponseHost } from "./credentialsMetaResponseHost"; + +export interface CredentialsMetaResponse { + id: string; + provider: string; + type: CredentialsMetaResponseType; + title: CredentialsMetaResponseTitle; + scopes: CredentialsMetaResponseScopes; + username: CredentialsMetaResponseUsername; + /** Host pattern for host-scoped credentials */ + host?: CredentialsMetaResponseHost; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsMetaResponseHost.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsMetaResponseHost.ts new file mode 100644 index 0000000000..d6b4a38cb3 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsMetaResponseHost.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +/** + * Host pattern for host-scoped credentials + */ +export type CredentialsMetaResponseHost = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsMetaResponseScopes.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsMetaResponseScopes.ts new file mode 100644 index 0000000000..fe012b2bc2 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsMetaResponseScopes.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type CredentialsMetaResponseScopes = string[] | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsMetaResponseTitle.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsMetaResponseTitle.ts new file mode 100644 index 0000000000..915705495b --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsMetaResponseTitle.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type CredentialsMetaResponseTitle = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsMetaResponseType.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsMetaResponseType.ts new file mode 100644 index 0000000000..a8b1fce8dc --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsMetaResponseType.ts @@ -0,0 +1,18 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type CredentialsMetaResponseType = + (typeof CredentialsMetaResponseType)[keyof typeof CredentialsMetaResponseType]; + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export const CredentialsMetaResponseType = { + api_key: "api_key", + oauth2: "oauth2", + user_password: "user_password", + host_scoped: "host_scoped", +} as const; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsMetaResponseUsername.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsMetaResponseUsername.ts new file mode 100644 index 0000000000..79c173f738 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/credentialsMetaResponseUsername.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type CredentialsMetaResponseUsername = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/creditTransactionType.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/creditTransactionType.ts new file mode 100644 index 0000000000..030b6258a6 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/creditTransactionType.ts @@ -0,0 +1,19 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type CreditTransactionType = + (typeof CreditTransactionType)[keyof typeof CreditTransactionType]; + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export const CreditTransactionType = { + TOP_UP: "TOP_UP", + USAGE: "USAGE", + GRANT: "GRANT", + REFUND: "REFUND", + CARD_CHECK: "CARD_CHECK", +} as const; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/deleteGraphResponse.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/deleteGraphResponse.ts new file mode 100644 index 0000000000..202d0d83af --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/deleteGraphResponse.ts @@ -0,0 +1,11 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export interface DeleteGraphResponse { + version_counts: number; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/deleteV1DeleteCredentials200.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/deleteV1DeleteCredentials200.ts new file mode 100644 index 0000000000..0673a0189f --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/deleteV1DeleteCredentials200.ts @@ -0,0 +1,13 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { CredentialsDeletionResponse } from "./credentialsDeletionResponse"; +import type { CredentialsDeletionNeedsConfirmationResponse } from "./credentialsDeletionNeedsConfirmationResponse"; + +export type DeleteV1DeleteCredentials200 = + | CredentialsDeletionResponse + | CredentialsDeletionNeedsConfirmationResponse; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/deleteV1DeleteCredentialsParams.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/deleteV1DeleteCredentialsParams.ts new file mode 100644 index 0000000000..33a1c58020 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/deleteV1DeleteCredentialsParams.ts @@ -0,0 +1,11 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type DeleteV1DeleteCredentialsParams = { + force?: boolean; +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/deleteV1DeleteExecutionSchedule200.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/deleteV1DeleteExecutionSchedule200.ts new file mode 100644 index 0000000000..9f19067bc1 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/deleteV1DeleteExecutionSchedule200.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type DeleteV1DeleteExecutionSchedule200 = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/document.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/document.ts new file mode 100644 index 0000000000..f65ea6887f --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/document.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export interface Document { + url: string; + relevance_score: number; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/executeGraphResponse.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/executeGraphResponse.ts new file mode 100644 index 0000000000..28971aec93 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/executeGraphResponse.ts @@ -0,0 +1,11 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export interface ExecuteGraphResponse { + graph_exec_id: string; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/getV1GetCredential200.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/getV1GetCredential200.ts new file mode 100644 index 0000000000..2cba7f9def --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/getV1GetCredential200.ts @@ -0,0 +1,17 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { OAuth2Credentials } from "./oAuth2Credentials"; +import type { APIKeyCredentials } from "./aPIKeyCredentials"; +import type { UserPasswordCredentials } from "./userPasswordCredentials"; +import type { HostScopedCredentialsOutput } from "./hostScopedCredentialsOutput"; + +export type GetV1GetCredential200 = + | OAuth2Credentials + | APIKeyCredentials + | UserPasswordCredentials + | HostScopedCredentialsOutput; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/getV1GetCreditHistoryParams.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/getV1GetCreditHistoryParams.ts new file mode 100644 index 0000000000..90c8ecd364 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/getV1GetCreditHistoryParams.ts @@ -0,0 +1,13 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type GetV1GetCreditHistoryParams = { + transaction_time?: string | null; + transaction_type?: string | null; + transaction_count_limit?: number; +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/getV1GetExecutionDetails200.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/getV1GetExecutionDetails200.ts new file mode 100644 index 0000000000..2bbd8a1492 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/getV1GetExecutionDetails200.ts @@ -0,0 +1,13 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { GraphExecution } from "./graphExecution"; +import type { GraphExecutionWithNodes } from "./graphExecutionWithNodes"; + +export type GetV1GetExecutionDetails200 = + | GraphExecution + | GraphExecutionWithNodes; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/getV1GetGraphVersionParams.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/getV1GetGraphVersionParams.ts new file mode 100644 index 0000000000..521a8754ac --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/getV1GetGraphVersionParams.ts @@ -0,0 +1,11 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type GetV1GetGraphVersionParams = { + for_export?: boolean; +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/getV1GetSpecificGraphParams.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/getV1GetSpecificGraphParams.ts new file mode 100644 index 0000000000..e30abfea86 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/getV1GetSpecificGraphParams.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type GetV1GetSpecificGraphParams = { + version?: number | null; + for_export?: boolean; +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/getV1GetUserCredits200.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/getV1GetUserCredits200.ts new file mode 100644 index 0000000000..fb035936b6 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/getV1GetUserCredits200.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type GetV1GetUserCredits200 = { [key: string]: number }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/getV1ListAvailableBlocks200Item.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/getV1ListAvailableBlocks200Item.ts new file mode 100644 index 0000000000..7a6892363d --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/getV1ListAvailableBlocks200Item.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type GetV1ListAvailableBlocks200Item = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/getV1ListUserApiKeys200.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/getV1ListUserApiKeys200.ts new file mode 100644 index 0000000000..e919f16d5b --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/getV1ListUserApiKeys200.ts @@ -0,0 +1,13 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { APIKeyWithoutHash } from "./aPIKeyWithoutHash"; +import type { GetV1ListUserApiKeys200AnyOf } from "./getV1ListUserApiKeys200AnyOf"; + +export type GetV1ListUserApiKeys200 = + | APIKeyWithoutHash[] + | GetV1ListUserApiKeys200AnyOf; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/getV1ListUserApiKeys200AnyOf.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/getV1ListUserApiKeys200AnyOf.ts new file mode 100644 index 0000000000..39163009f0 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/getV1ListUserApiKeys200AnyOf.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type GetV1ListUserApiKeys200AnyOf = { [key: string]: string }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/getV1LoginParams.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/getV1LoginParams.ts new file mode 100644 index 0000000000..1b87a2aa67 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/getV1LoginParams.ts @@ -0,0 +1,11 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type GetV1LoginParams = { + scopes?: string; +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/getV1ManagePaymentMethods200.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/getV1ManagePaymentMethods200.ts new file mode 100644 index 0000000000..9668a380fa --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/getV1ManagePaymentMethods200.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type GetV1ManagePaymentMethods200 = { [key: string]: string }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/getV2GetAdminListingsHistoryParams.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/getV2GetAdminListingsHistoryParams.ts new file mode 100644 index 0000000000..44e04a8175 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/getV2GetAdminListingsHistoryParams.ts @@ -0,0 +1,15 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { SubmissionStatus } from "./submissionStatus"; + +export type GetV2GetAdminListingsHistoryParams = { + status?: SubmissionStatus | null; + search?: string | null; + page?: number; + page_size?: number; +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/getV2GetAgentByStoreId200.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/getV2GetAgentByStoreId200.ts new file mode 100644 index 0000000000..350cf20566 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/getV2GetAgentByStoreId200.ts @@ -0,0 +1,10 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { LibraryAgent } from "./libraryAgent"; + +export type GetV2GetAgentByStoreId200 = LibraryAgent | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/getV2GetAllUsersHistoryParams.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/getV2GetAllUsersHistoryParams.ts new file mode 100644 index 0000000000..2b1a82a100 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/getV2GetAllUsersHistoryParams.ts @@ -0,0 +1,15 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { CreditTransactionType } from "./creditTransactionType"; + +export type GetV2GetAllUsersHistoryParams = { + search?: string | null; + page?: number; + page_size?: number; + transaction_filter?: CreditTransactionType | null; +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/getV2GetLibraryAgentByGraphIdParams.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/getV2GetLibraryAgentByGraphIdParams.ts new file mode 100644 index 0000000000..0bb87f620a --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/getV2GetLibraryAgentByGraphIdParams.ts @@ -0,0 +1,11 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type GetV2GetLibraryAgentByGraphIdParams = { + version?: number | null; +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/getV2ListLibraryAgentsParams.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/getV2ListLibraryAgentsParams.ts new file mode 100644 index 0000000000..df934ad491 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/getV2ListLibraryAgentsParams.ts @@ -0,0 +1,29 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { LibraryAgentSort } from "./libraryAgentSort"; + +export type GetV2ListLibraryAgentsParams = { + /** + * Search term to filter agents + */ + search_term?: string | null; + /** + * Criteria to sort results by + */ + sort_by?: LibraryAgentSort; + /** + * Page number to retrieve (must be >= 1) + * @minimum 1 + */ + page?: number; + /** + * Number of agents per page (must be >= 1) + * @minimum 1 + */ + page_size?: number; +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/getV2ListMySubmissionsParams.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/getV2ListMySubmissionsParams.ts new file mode 100644 index 0000000000..515a5f04b8 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/getV2ListMySubmissionsParams.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type GetV2ListMySubmissionsParams = { + page?: number; + page_size?: number; +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/getV2ListPresetsParams.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/getV2ListPresetsParams.ts new file mode 100644 index 0000000000..cf7fc3b2b6 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/getV2ListPresetsParams.ts @@ -0,0 +1,22 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type GetV2ListPresetsParams = { + /** + * @minimum 1 + */ + page?: number; + /** + * @minimum 1 + */ + page_size?: number; + /** + * Allows to filter presets by a specific agent graph + */ + graph_id: string | null; +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/getV2ListStoreAgentsParams.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/getV2ListStoreAgentsParams.ts new file mode 100644 index 0000000000..dc3461ec61 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/getV2ListStoreAgentsParams.ts @@ -0,0 +1,17 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type GetV2ListStoreAgentsParams = { + featured?: boolean; + creator?: string | null; + sorted_by?: string | null; + search_query?: string | null; + category?: string | null; + page?: number; + page_size?: number; +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/getV2ListStoreCreatorsParams.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/getV2ListStoreCreatorsParams.ts new file mode 100644 index 0000000000..61dfb39be0 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/getV2ListStoreCreatorsParams.ts @@ -0,0 +1,15 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type GetV2ListStoreCreatorsParams = { + featured?: boolean; + search_query?: string | null; + sorted_by?: string | null; + page?: number; + page_size?: number; +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/graph.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/graph.ts new file mode 100644 index 0000000000..02516bee1f --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/graph.ts @@ -0,0 +1,25 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { Node } from "./node"; +import type { Link } from "./link"; +import type { GraphForkedFromId } from "./graphForkedFromId"; +import type { GraphForkedFromVersion } from "./graphForkedFromVersion"; +import type { BaseGraphInput } from "./baseGraphInput"; + +export interface Graph { + id?: string; + version?: number; + is_active?: boolean; + name: string; + description: string; + nodes?: Node[]; + links?: Link[]; + forked_from_id?: GraphForkedFromId; + forked_from_version?: GraphForkedFromVersion; + sub_graphs?: BaseGraphInput[]; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecution.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecution.ts new file mode 100644 index 0000000000..34ddcf3c3a --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecution.ts @@ -0,0 +1,26 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { GraphExecutionPresetId } from "./graphExecutionPresetId"; +import type { AgentExecutionStatus } from "./agentExecutionStatus"; +import type { GraphExecutionStats } from "./graphExecutionStats"; +import type { GraphExecutionInputs } from "./graphExecutionInputs"; +import type { GraphExecutionOutputs } from "./graphExecutionOutputs"; + +export interface GraphExecution { + id?: string; + user_id: string; + graph_id: string; + graph_version: number; + preset_id?: GraphExecutionPresetId; + status: AgentExecutionStatus; + started_at: string; + ended_at: string; + stats: GraphExecutionStats; + inputs: GraphExecutionInputs; + outputs: GraphExecutionOutputs; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionInputs.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionInputs.ts new file mode 100644 index 0000000000..9fcd2c1d52 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionInputs.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type GraphExecutionInputs = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionJobInfo.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionJobInfo.ts new file mode 100644 index 0000000000..e195389275 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionJobInfo.ts @@ -0,0 +1,21 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { GraphExecutionJobInfoInputData } from "./graphExecutionJobInfoInputData"; +import type { GraphExecutionJobInfoInputCredentials } from "./graphExecutionJobInfoInputCredentials"; + +export interface GraphExecutionJobInfo { + user_id: string; + graph_id: string; + graph_version: number; + cron: string; + input_data: GraphExecutionJobInfoInputData; + input_credentials?: GraphExecutionJobInfoInputCredentials; + id: string; + name: string; + next_run_time: string; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionJobInfoInputCredentials.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionJobInfoInputCredentials.ts new file mode 100644 index 0000000000..1f24a877d5 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionJobInfoInputCredentials.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { CredentialsMetaInput } from "./credentialsMetaInput"; + +export type GraphExecutionJobInfoInputCredentials = { + [key: string]: CredentialsMetaInput; +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionJobInfoInputData.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionJobInfoInputData.ts new file mode 100644 index 0000000000..270e530af5 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionJobInfoInputData.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type GraphExecutionJobInfoInputData = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionMeta.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionMeta.ts new file mode 100644 index 0000000000..387ccd4a65 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionMeta.ts @@ -0,0 +1,22 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { GraphExecutionMetaPresetId } from "./graphExecutionMetaPresetId"; +import type { AgentExecutionStatus } from "./agentExecutionStatus"; +import type { GraphExecutionMetaStats } from "./graphExecutionMetaStats"; + +export interface GraphExecutionMeta { + id?: string; + user_id: string; + graph_id: string; + graph_version: number; + preset_id?: GraphExecutionMetaPresetId; + status: AgentExecutionStatus; + started_at: string; + ended_at: string; + stats: GraphExecutionMetaStats; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionMetaPresetId.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionMetaPresetId.ts new file mode 100644 index 0000000000..d2db05f894 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionMetaPresetId.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type GraphExecutionMetaPresetId = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionMetaStats.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionMetaStats.ts new file mode 100644 index 0000000000..db50ec5495 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionMetaStats.ts @@ -0,0 +1,10 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { Stats } from "./stats"; + +export type GraphExecutionMetaStats = Stats | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionOutputs.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionOutputs.ts new file mode 100644 index 0000000000..52b6d3ea17 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionOutputs.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type GraphExecutionOutputs = { [key: string]: unknown[] }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionPresetId.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionPresetId.ts new file mode 100644 index 0000000000..42789ed3d7 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionPresetId.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type GraphExecutionPresetId = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionStats.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionStats.ts new file mode 100644 index 0000000000..0db91702a1 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionStats.ts @@ -0,0 +1,10 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { Stats } from "./stats"; + +export type GraphExecutionStats = Stats | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionWithNodes.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionWithNodes.ts new file mode 100644 index 0000000000..e0d552ad64 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionWithNodes.ts @@ -0,0 +1,28 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { GraphExecutionWithNodesPresetId } from "./graphExecutionWithNodesPresetId"; +import type { AgentExecutionStatus } from "./agentExecutionStatus"; +import type { GraphExecutionWithNodesStats } from "./graphExecutionWithNodesStats"; +import type { GraphExecutionWithNodesInputs } from "./graphExecutionWithNodesInputs"; +import type { GraphExecutionWithNodesOutputs } from "./graphExecutionWithNodesOutputs"; +import type { NodeExecutionResult } from "./nodeExecutionResult"; + +export interface GraphExecutionWithNodes { + id?: string; + user_id: string; + graph_id: string; + graph_version: number; + preset_id?: GraphExecutionWithNodesPresetId; + status: AgentExecutionStatus; + started_at: string; + ended_at: string; + stats: GraphExecutionWithNodesStats; + inputs: GraphExecutionWithNodesInputs; + outputs: GraphExecutionWithNodesOutputs; + node_executions: NodeExecutionResult[]; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionWithNodesInputs.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionWithNodesInputs.ts new file mode 100644 index 0000000000..33beb34983 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionWithNodesInputs.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type GraphExecutionWithNodesInputs = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionWithNodesOutputs.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionWithNodesOutputs.ts new file mode 100644 index 0000000000..4b8169fb37 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionWithNodesOutputs.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type GraphExecutionWithNodesOutputs = { [key: string]: unknown[] }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionWithNodesPresetId.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionWithNodesPresetId.ts new file mode 100644 index 0000000000..b212be4963 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionWithNodesPresetId.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type GraphExecutionWithNodesPresetId = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionWithNodesStats.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionWithNodesStats.ts new file mode 100644 index 0000000000..73164b7376 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/graphExecutionWithNodesStats.ts @@ -0,0 +1,10 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { Stats } from "./stats"; + +export type GraphExecutionWithNodesStats = Stats | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/graphForkedFromId.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/graphForkedFromId.ts new file mode 100644 index 0000000000..1b1f6e28a7 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/graphForkedFromId.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type GraphForkedFromId = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/graphForkedFromVersion.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/graphForkedFromVersion.ts new file mode 100644 index 0000000000..1d2d2f4ac4 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/graphForkedFromVersion.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type GraphForkedFromVersion = number | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/graphModel.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/graphModel.ts new file mode 100644 index 0000000000..aa7a59e662 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/graphModel.ts @@ -0,0 +1,33 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { NodeModel } from "./nodeModel"; +import type { Link } from "./link"; +import type { GraphModelForkedFromId } from "./graphModelForkedFromId"; +import type { GraphModelForkedFromVersion } from "./graphModelForkedFromVersion"; +import type { BaseGraphOutput } from "./baseGraphOutput"; +import type { GraphModelInputSchema } from "./graphModelInputSchema"; +import type { GraphModelOutputSchema } from "./graphModelOutputSchema"; +import type { GraphModelCredentialsInputSchema } from "./graphModelCredentialsInputSchema"; + +export interface GraphModel { + id?: string; + version?: number; + is_active?: boolean; + name: string; + description: string; + nodes?: NodeModel[]; + links?: Link[]; + forked_from_id?: GraphModelForkedFromId; + forked_from_version?: GraphModelForkedFromVersion; + sub_graphs?: BaseGraphOutput[]; + user_id: string; + readonly input_schema: GraphModelInputSchema; + readonly output_schema: GraphModelOutputSchema; + readonly credentials_input_schema: GraphModelCredentialsInputSchema; + readonly has_webhook_trigger: boolean; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/graphModelCredentialsInputSchema.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/graphModelCredentialsInputSchema.ts new file mode 100644 index 0000000000..90ce351a5b --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/graphModelCredentialsInputSchema.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type GraphModelCredentialsInputSchema = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/graphModelForkedFromId.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/graphModelForkedFromId.ts new file mode 100644 index 0000000000..fcf3d0efa8 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/graphModelForkedFromId.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type GraphModelForkedFromId = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/graphModelForkedFromVersion.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/graphModelForkedFromVersion.ts new file mode 100644 index 0000000000..8b4f889115 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/graphModelForkedFromVersion.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type GraphModelForkedFromVersion = number | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/graphModelInputSchema.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/graphModelInputSchema.ts new file mode 100644 index 0000000000..76da0b5642 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/graphModelInputSchema.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type GraphModelInputSchema = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/graphModelOutputSchema.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/graphModelOutputSchema.ts new file mode 100644 index 0000000000..8d2b9560a2 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/graphModelOutputSchema.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type GraphModelOutputSchema = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/hTTPValidationError.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/hTTPValidationError.ts new file mode 100644 index 0000000000..b216ede240 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/hTTPValidationError.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { ValidationError } from "./validationError"; + +export interface HTTPValidationError { + detail?: ValidationError[]; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/hostScopedCredentialsInput.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/hostScopedCredentialsInput.ts new file mode 100644 index 0000000000..e46e6d7979 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/hostScopedCredentialsInput.ts @@ -0,0 +1,20 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { HostScopedCredentialsInputTitle } from "./hostScopedCredentialsInputTitle"; +import type { HostScopedCredentialsInputHeaders } from "./hostScopedCredentialsInputHeaders"; + +export interface HostScopedCredentialsInput { + id?: string; + provider: string; + title?: HostScopedCredentialsInputTitle; + type?: "host_scoped"; + /** The host/URI pattern to match against request URLs */ + host: string; + /** Key-value header map to add to matching requests */ + headers?: HostScopedCredentialsInputHeaders; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/hostScopedCredentialsInputHeaders.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/hostScopedCredentialsInputHeaders.ts new file mode 100644 index 0000000000..97971ba57c --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/hostScopedCredentialsInputHeaders.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +/** + * Key-value header map to add to matching requests + */ +export type HostScopedCredentialsInputHeaders = { [key: string]: string }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/hostScopedCredentialsInputTitle.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/hostScopedCredentialsInputTitle.ts new file mode 100644 index 0000000000..718198d2b3 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/hostScopedCredentialsInputTitle.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type HostScopedCredentialsInputTitle = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/hostScopedCredentialsOutput.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/hostScopedCredentialsOutput.ts new file mode 100644 index 0000000000..2fe39782db --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/hostScopedCredentialsOutput.ts @@ -0,0 +1,20 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { HostScopedCredentialsOutputTitle } from "./hostScopedCredentialsOutputTitle"; +import type { HostScopedCredentialsOutputHeaders } from "./hostScopedCredentialsOutputHeaders"; + +export interface HostScopedCredentialsOutput { + id?: string; + provider: string; + title?: HostScopedCredentialsOutputTitle; + type?: "host_scoped"; + /** The host/URI pattern to match against request URLs */ + host: string; + /** Key-value header map to add to matching requests */ + headers?: HostScopedCredentialsOutputHeaders; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/hostScopedCredentialsOutputHeaders.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/hostScopedCredentialsOutputHeaders.ts new file mode 100644 index 0000000000..d5962d20bb --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/hostScopedCredentialsOutputHeaders.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +/** + * Key-value header map to add to matching requests + */ +export type HostScopedCredentialsOutputHeaders = { [key: string]: string }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/hostScopedCredentialsOutputTitle.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/hostScopedCredentialsOutputTitle.ts new file mode 100644 index 0000000000..bc96d6f738 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/hostScopedCredentialsOutputTitle.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type HostScopedCredentialsOutputTitle = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgent.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgent.ts new file mode 100644 index 0000000000..583be8150b --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgent.ts @@ -0,0 +1,38 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { LibraryAgentImageUrl } from "./libraryAgentImageUrl"; +import type { LibraryAgentStatus } from "./libraryAgentStatus"; +import type { LibraryAgentInputSchema } from "./libraryAgentInputSchema"; +import type { LibraryAgentCredentialsInputSchema } from "./libraryAgentCredentialsInputSchema"; +import type { LibraryAgentTriggerSetupInfo } from "./libraryAgentTriggerSetupInfo"; + +/** + * Represents an agent in the library, including metadata for display and +user interaction within the system. + */ +export interface LibraryAgent { + id: string; + graph_id: string; + graph_version: number; + image_url: LibraryAgentImageUrl; + creator_name: string; + creator_image_url: string; + status: LibraryAgentStatus; + updated_at: string; + name: string; + description: string; + input_schema: LibraryAgentInputSchema; + /** Input schema for credentials required by the agent */ + credentials_input_schema: LibraryAgentCredentialsInputSchema; + /** Whether the agent has an external trigger (e.g. webhook) node */ + has_external_trigger: boolean; + trigger_setup_info?: LibraryAgentTriggerSetupInfo; + new_output: boolean; + can_access_graph: boolean; + is_latest_version: boolean; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentCredentialsInputSchema.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentCredentialsInputSchema.ts new file mode 100644 index 0000000000..8c957f3be5 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentCredentialsInputSchema.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +/** + * Input schema for credentials required by the agent + */ +export type LibraryAgentCredentialsInputSchema = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentImageUrl.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentImageUrl.ts new file mode 100644 index 0000000000..cf329fdbfc --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentImageUrl.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type LibraryAgentImageUrl = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentInputSchema.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentInputSchema.ts new file mode 100644 index 0000000000..d2e5e527c9 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentInputSchema.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type LibraryAgentInputSchema = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPreset.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPreset.ts new file mode 100644 index 0000000000..ce067a2850 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPreset.ts @@ -0,0 +1,27 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { LibraryAgentPresetInputs } from "./libraryAgentPresetInputs"; +import type { LibraryAgentPresetCredentials } from "./libraryAgentPresetCredentials"; +import type { LibraryAgentPresetWebhookId } from "./libraryAgentPresetWebhookId"; + +/** + * Represents a preset configuration for a library agent. + */ +export interface LibraryAgentPreset { + graph_id: string; + graph_version: number; + inputs: LibraryAgentPresetInputs; + credentials: LibraryAgentPresetCredentials; + name: string; + description: string; + is_active?: boolean; + webhook_id?: LibraryAgentPresetWebhookId; + id: string; + user_id: string; + updated_at: string; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetCreatable.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetCreatable.ts new file mode 100644 index 0000000000..38f9c1ed53 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetCreatable.ts @@ -0,0 +1,24 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { LibraryAgentPresetCreatableInputs } from "./libraryAgentPresetCreatableInputs"; +import type { LibraryAgentPresetCreatableCredentials } from "./libraryAgentPresetCreatableCredentials"; +import type { LibraryAgentPresetCreatableWebhookId } from "./libraryAgentPresetCreatableWebhookId"; + +/** + * Request model used when creating a new preset for a library agent. + */ +export interface LibraryAgentPresetCreatable { + graph_id: string; + graph_version: number; + inputs: LibraryAgentPresetCreatableInputs; + credentials: LibraryAgentPresetCreatableCredentials; + name: string; + description: string; + is_active?: boolean; + webhook_id?: LibraryAgentPresetCreatableWebhookId; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetCreatableCredentials.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetCreatableCredentials.ts new file mode 100644 index 0000000000..857ff545db --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetCreatableCredentials.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { CredentialsMetaInput } from "./credentialsMetaInput"; + +export type LibraryAgentPresetCreatableCredentials = { + [key: string]: CredentialsMetaInput; +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetCreatableFromGraphExecution.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetCreatableFromGraphExecution.ts new file mode 100644 index 0000000000..90aea688b4 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetCreatableFromGraphExecution.ts @@ -0,0 +1,17 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +/** + * Request model used when creating a new preset for a library agent. + */ +export interface LibraryAgentPresetCreatableFromGraphExecution { + graph_execution_id: string; + name: string; + description: string; + is_active?: boolean; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetCreatableInputs.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetCreatableInputs.ts new file mode 100644 index 0000000000..f1d90905ae --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetCreatableInputs.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type LibraryAgentPresetCreatableInputs = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetCreatableWebhookId.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetCreatableWebhookId.ts new file mode 100644 index 0000000000..9b35cd693f --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetCreatableWebhookId.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type LibraryAgentPresetCreatableWebhookId = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetCredentials.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetCredentials.ts new file mode 100644 index 0000000000..f583a776fa --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetCredentials.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { CredentialsMetaInput } from "./credentialsMetaInput"; + +export type LibraryAgentPresetCredentials = { + [key: string]: CredentialsMetaInput; +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetInputs.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetInputs.ts new file mode 100644 index 0000000000..ad5ee445bb --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetInputs.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type LibraryAgentPresetInputs = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetResponse.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetResponse.ts new file mode 100644 index 0000000000..2f24c2dff4 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetResponse.ts @@ -0,0 +1,17 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { LibraryAgentPreset } from "./libraryAgentPreset"; +import type { Pagination } from "./pagination"; + +/** + * Response schema for a list of agent presets and pagination info. + */ +export interface LibraryAgentPresetResponse { + presets: LibraryAgentPreset[]; + pagination: Pagination; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetUpdatable.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetUpdatable.ts new file mode 100644 index 0000000000..672420da8e --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetUpdatable.ts @@ -0,0 +1,23 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { LibraryAgentPresetUpdatableInputs } from "./libraryAgentPresetUpdatableInputs"; +import type { LibraryAgentPresetUpdatableCredentials } from "./libraryAgentPresetUpdatableCredentials"; +import type { LibraryAgentPresetUpdatableName } from "./libraryAgentPresetUpdatableName"; +import type { LibraryAgentPresetUpdatableDescription } from "./libraryAgentPresetUpdatableDescription"; +import type { LibraryAgentPresetUpdatableIsActive } from "./libraryAgentPresetUpdatableIsActive"; + +/** + * Request model used when updating a preset for a library agent. + */ +export interface LibraryAgentPresetUpdatable { + inputs?: LibraryAgentPresetUpdatableInputs; + credentials?: LibraryAgentPresetUpdatableCredentials; + name?: LibraryAgentPresetUpdatableName; + description?: LibraryAgentPresetUpdatableDescription; + is_active?: LibraryAgentPresetUpdatableIsActive; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetUpdatableCredentials.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetUpdatableCredentials.ts new file mode 100644 index 0000000000..a30e3377bb --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetUpdatableCredentials.ts @@ -0,0 +1,11 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { LibraryAgentPresetUpdatableCredentialsAnyOf } from "./libraryAgentPresetUpdatableCredentialsAnyOf"; + +export type LibraryAgentPresetUpdatableCredentials = + LibraryAgentPresetUpdatableCredentialsAnyOf | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetUpdatableCredentialsAnyOf.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetUpdatableCredentialsAnyOf.ts new file mode 100644 index 0000000000..d36196eb05 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetUpdatableCredentialsAnyOf.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { CredentialsMetaInput } from "./credentialsMetaInput"; + +export type LibraryAgentPresetUpdatableCredentialsAnyOf = { + [key: string]: CredentialsMetaInput; +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetUpdatableDescription.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetUpdatableDescription.ts new file mode 100644 index 0000000000..485a8363f2 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetUpdatableDescription.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type LibraryAgentPresetUpdatableDescription = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetUpdatableInputs.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetUpdatableInputs.ts new file mode 100644 index 0000000000..487b08ab1a --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetUpdatableInputs.ts @@ -0,0 +1,11 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { LibraryAgentPresetUpdatableInputsAnyOf } from "./libraryAgentPresetUpdatableInputsAnyOf"; + +export type LibraryAgentPresetUpdatableInputs = + LibraryAgentPresetUpdatableInputsAnyOf | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetUpdatableInputsAnyOf.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetUpdatableInputsAnyOf.ts new file mode 100644 index 0000000000..96010a9fb0 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetUpdatableInputsAnyOf.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type LibraryAgentPresetUpdatableInputsAnyOf = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetUpdatableIsActive.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetUpdatableIsActive.ts new file mode 100644 index 0000000000..de763759d8 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetUpdatableIsActive.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type LibraryAgentPresetUpdatableIsActive = boolean | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetUpdatableName.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetUpdatableName.ts new file mode 100644 index 0000000000..450e5a964f --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetUpdatableName.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type LibraryAgentPresetUpdatableName = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetWebhookId.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetWebhookId.ts new file mode 100644 index 0000000000..c09d7ad733 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentPresetWebhookId.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type LibraryAgentPresetWebhookId = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentResponse.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentResponse.ts new file mode 100644 index 0000000000..2bc52229d5 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentResponse.ts @@ -0,0 +1,17 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { LibraryAgent } from "./libraryAgent"; +import type { Pagination } from "./pagination"; + +/** + * Response schema for a list of library agents and pagination info. + */ +export interface LibraryAgentResponse { + agents: LibraryAgent[]; + pagination: Pagination; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentSort.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentSort.ts new file mode 100644 index 0000000000..9d077ad28e --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentSort.ts @@ -0,0 +1,19 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +/** + * Possible sort options for sorting library agents. + */ +export type LibraryAgentSort = + (typeof LibraryAgentSort)[keyof typeof LibraryAgentSort]; + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export const LibraryAgentSort = { + createdAt: "createdAt", + updatedAt: "updatedAt", +} as const; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentStatus.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentStatus.ts new file mode 100644 index 0000000000..f47b34fce1 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentStatus.ts @@ -0,0 +1,18 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type LibraryAgentStatus = + (typeof LibraryAgentStatus)[keyof typeof LibraryAgentStatus]; + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export const LibraryAgentStatus = { + COMPLETED: "COMPLETED", + HEALTHY: "HEALTHY", + WAITING: "WAITING", + ERROR: "ERROR", +} as const; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentTriggerInfo.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentTriggerInfo.ts new file mode 100644 index 0000000000..5f8338f9b0 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentTriggerInfo.ts @@ -0,0 +1,17 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { ProviderName } from "./providerName"; +import type { LibraryAgentTriggerInfoConfigSchema } from "./libraryAgentTriggerInfoConfigSchema"; +import type { LibraryAgentTriggerInfoCredentialsInputName } from "./libraryAgentTriggerInfoCredentialsInputName"; + +export interface LibraryAgentTriggerInfo { + provider: ProviderName; + /** Input schema for the trigger block */ + config_schema: LibraryAgentTriggerInfoConfigSchema; + credentials_input_name: LibraryAgentTriggerInfoCredentialsInputName; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentTriggerInfoConfigSchema.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentTriggerInfoConfigSchema.ts new file mode 100644 index 0000000000..2d002f0d4c --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentTriggerInfoConfigSchema.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +/** + * Input schema for the trigger block + */ +export type LibraryAgentTriggerInfoConfigSchema = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentTriggerInfoCredentialsInputName.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentTriggerInfoCredentialsInputName.ts new file mode 100644 index 0000000000..3915d46216 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentTriggerInfoCredentialsInputName.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type LibraryAgentTriggerInfoCredentialsInputName = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentTriggerSetupInfo.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentTriggerSetupInfo.ts new file mode 100644 index 0000000000..2f7bb2bf3d --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentTriggerSetupInfo.ts @@ -0,0 +1,10 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { LibraryAgentTriggerInfo } from "./libraryAgentTriggerInfo"; + +export type LibraryAgentTriggerSetupInfo = LibraryAgentTriggerInfo | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentUpdateRequest.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentUpdateRequest.ts new file mode 100644 index 0000000000..17185d05ac --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentUpdateRequest.ts @@ -0,0 +1,25 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { LibraryAgentUpdateRequestAutoUpdateVersion } from "./libraryAgentUpdateRequestAutoUpdateVersion"; +import type { LibraryAgentUpdateRequestIsFavorite } from "./libraryAgentUpdateRequestIsFavorite"; +import type { LibraryAgentUpdateRequestIsArchived } from "./libraryAgentUpdateRequestIsArchived"; + +/** + * Schema for updating a library agent via PUT. + +Includes flags for auto-updating version, marking as favorite, +archiving, or deleting. + */ +export interface LibraryAgentUpdateRequest { + /** Auto-update the agent version */ + auto_update_version?: LibraryAgentUpdateRequestAutoUpdateVersion; + /** Mark the agent as a favorite */ + is_favorite?: LibraryAgentUpdateRequestIsFavorite; + /** Archive the agent */ + is_archived?: LibraryAgentUpdateRequestIsArchived; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentUpdateRequestAutoUpdateVersion.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentUpdateRequestAutoUpdateVersion.ts new file mode 100644 index 0000000000..c3760774bf --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentUpdateRequestAutoUpdateVersion.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +/** + * Auto-update the agent version + */ +export type LibraryAgentUpdateRequestAutoUpdateVersion = boolean | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentUpdateRequestIsArchived.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentUpdateRequestIsArchived.ts new file mode 100644 index 0000000000..1bcc3b1032 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentUpdateRequestIsArchived.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +/** + * Archive the agent + */ +export type LibraryAgentUpdateRequestIsArchived = boolean | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentUpdateRequestIsFavorite.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentUpdateRequestIsFavorite.ts new file mode 100644 index 0000000000..8c8af320aa --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/libraryAgentUpdateRequestIsFavorite.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +/** + * Mark the agent as a favorite + */ +export type LibraryAgentUpdateRequestIsFavorite = boolean | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/link.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/link.ts new file mode 100644 index 0000000000..a23f8a1c47 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/link.ts @@ -0,0 +1,16 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export interface Link { + id?: string; + source_id: string; + sink_id: string; + source_name: string; + sink_name: string; + is_static?: boolean; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/logRawMetricRequest.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/logRawMetricRequest.ts new file mode 100644 index 0000000000..89e0f1037e --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/logRawMetricRequest.ts @@ -0,0 +1,15 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export interface LogRawMetricRequest { + /** @minLength 1 */ + metric_name: string; + metric_value: number; + /** @minLength 1 */ + data_string: string; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/loginResponse.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/loginResponse.ts new file mode 100644 index 0000000000..59d221aec2 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/loginResponse.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export interface LoginResponse { + login_url: string; + state_token: string; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/message.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/message.ts new file mode 100644 index 0000000000..488998be60 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/message.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export interface Message { + query: string; + response: string; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/myAgent.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/myAgent.ts new file mode 100644 index 0000000000..d998aa62f4 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/myAgent.ts @@ -0,0 +1,17 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { MyAgentAgentImage } from "./myAgentAgentImage"; + +export interface MyAgent { + agent_id: string; + agent_version: number; + agent_name: string; + agent_image?: MyAgentAgentImage; + description: string; + last_edited: string; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/myAgentAgentImage.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/myAgentAgentImage.ts new file mode 100644 index 0000000000..36c088d6bb --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/myAgentAgentImage.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type MyAgentAgentImage = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/myAgentsResponse.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/myAgentsResponse.ts new file mode 100644 index 0000000000..ad054e547b --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/myAgentsResponse.ts @@ -0,0 +1,14 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { MyAgent } from "./myAgent"; +import type { Pagination } from "./pagination"; + +export interface MyAgentsResponse { + agents: MyAgent[]; + pagination: Pagination; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/node.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/node.ts new file mode 100644 index 0000000000..9f3740a02b --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/node.ts @@ -0,0 +1,19 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { NodeInputDefault } from "./nodeInputDefault"; +import type { NodeMetadata } from "./nodeMetadata"; +import type { Link } from "./link"; + +export interface Node { + id?: string; + block_id: string; + input_default?: NodeInputDefault; + metadata?: NodeMetadata; + input_links?: Link[]; + output_links?: Link[]; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/nodeExecutionResult.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/nodeExecutionResult.ts new file mode 100644 index 0000000000..b87ee7aa22 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/nodeExecutionResult.ts @@ -0,0 +1,30 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { AgentExecutionStatus } from "./agentExecutionStatus"; +import type { NodeExecutionResultInputData } from "./nodeExecutionResultInputData"; +import type { NodeExecutionResultOutputData } from "./nodeExecutionResultOutputData"; +import type { NodeExecutionResultQueueTime } from "./nodeExecutionResultQueueTime"; +import type { NodeExecutionResultStartTime } from "./nodeExecutionResultStartTime"; +import type { NodeExecutionResultEndTime } from "./nodeExecutionResultEndTime"; + +export interface NodeExecutionResult { + user_id: string; + graph_id: string; + graph_version: number; + graph_exec_id: string; + node_exec_id: string; + node_id: string; + block_id: string; + status: AgentExecutionStatus; + input_data: NodeExecutionResultInputData; + output_data: NodeExecutionResultOutputData; + add_time: string; + queue_time: NodeExecutionResultQueueTime; + start_time: NodeExecutionResultStartTime; + end_time: NodeExecutionResultEndTime; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/nodeExecutionResultEndTime.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/nodeExecutionResultEndTime.ts new file mode 100644 index 0000000000..e86cccf507 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/nodeExecutionResultEndTime.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type NodeExecutionResultEndTime = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/nodeExecutionResultInputData.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/nodeExecutionResultInputData.ts new file mode 100644 index 0000000000..9e4033a46d --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/nodeExecutionResultInputData.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type NodeExecutionResultInputData = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/nodeExecutionResultOutputData.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/nodeExecutionResultOutputData.ts new file mode 100644 index 0000000000..746d6e85e5 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/nodeExecutionResultOutputData.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type NodeExecutionResultOutputData = { [key: string]: unknown[] }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/nodeExecutionResultQueueTime.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/nodeExecutionResultQueueTime.ts new file mode 100644 index 0000000000..438572926a --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/nodeExecutionResultQueueTime.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type NodeExecutionResultQueueTime = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/nodeExecutionResultStartTime.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/nodeExecutionResultStartTime.ts new file mode 100644 index 0000000000..a8c389e19c --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/nodeExecutionResultStartTime.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type NodeExecutionResultStartTime = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/nodeInputDefault.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/nodeInputDefault.ts new file mode 100644 index 0000000000..191a06379d --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/nodeInputDefault.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type NodeInputDefault = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/nodeMetadata.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/nodeMetadata.ts new file mode 100644 index 0000000000..3b3ab525e6 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/nodeMetadata.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type NodeMetadata = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/nodeModel.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/nodeModel.ts new file mode 100644 index 0000000000..f81abc5f88 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/nodeModel.ts @@ -0,0 +1,25 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { NodeModelInputDefault } from "./nodeModelInputDefault"; +import type { NodeModelMetadata } from "./nodeModelMetadata"; +import type { Link } from "./link"; +import type { NodeModelWebhookId } from "./nodeModelWebhookId"; +import type { NodeModelWebhook } from "./nodeModelWebhook"; + +export interface NodeModel { + id?: string; + block_id: string; + input_default?: NodeModelInputDefault; + metadata?: NodeModelMetadata; + input_links?: Link[]; + output_links?: Link[]; + graph_id: string; + graph_version: number; + webhook_id?: NodeModelWebhookId; + webhook?: NodeModelWebhook; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/nodeModelInputDefault.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/nodeModelInputDefault.ts new file mode 100644 index 0000000000..4ec2ae5204 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/nodeModelInputDefault.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type NodeModelInputDefault = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/nodeModelMetadata.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/nodeModelMetadata.ts new file mode 100644 index 0000000000..8cf1fbdf95 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/nodeModelMetadata.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type NodeModelMetadata = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/nodeModelWebhook.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/nodeModelWebhook.ts new file mode 100644 index 0000000000..fe03e981e3 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/nodeModelWebhook.ts @@ -0,0 +1,10 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { Webhook } from "./webhook"; + +export type NodeModelWebhook = Webhook | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/nodeModelWebhookId.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/nodeModelWebhookId.ts new file mode 100644 index 0000000000..82eb4173a5 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/nodeModelWebhookId.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type NodeModelWebhookId = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/notificationPreference.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/notificationPreference.ts new file mode 100644 index 0000000000..08bedfc794 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/notificationPreference.ts @@ -0,0 +1,18 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { NotificationPreferencePreferences } from "./notificationPreferencePreferences"; + +export interface NotificationPreference { + user_id: string; + email: string; + /** Which notifications the user wants */ + preferences?: NotificationPreferencePreferences; + daily_limit?: number; + emails_sent_today?: number; + last_reset_date?: string; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/notificationPreferenceDTO.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/notificationPreferenceDTO.ts new file mode 100644 index 0000000000..97cefcc15c --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/notificationPreferenceDTO.ts @@ -0,0 +1,17 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { NotificationPreferenceDTOPreferences } from "./notificationPreferenceDTOPreferences"; + +export interface NotificationPreferenceDTO { + /** User's email address */ + email: string; + /** Which notifications the user wants */ + preferences: NotificationPreferenceDTOPreferences; + /** Max emails per day */ + daily_limit: number; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/notificationPreferenceDTOPreferences.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/notificationPreferenceDTOPreferences.ts new file mode 100644 index 0000000000..66c2762295 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/notificationPreferenceDTOPreferences.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +/** + * Which notifications the user wants + */ +export type NotificationPreferenceDTOPreferences = { [key: string]: boolean }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/notificationPreferencePreferences.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/notificationPreferencePreferences.ts new file mode 100644 index 0000000000..efdcd19d9c --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/notificationPreferencePreferences.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +/** + * Which notifications the user wants + */ +export type NotificationPreferencePreferences = { [key: string]: boolean }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/notificationType.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/notificationType.ts new file mode 100644 index 0000000000..2a57ec2170 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/notificationType.ts @@ -0,0 +1,24 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type NotificationType = + (typeof NotificationType)[keyof typeof NotificationType]; + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export const NotificationType = { + AGENT_RUN: "AGENT_RUN", + ZERO_BALANCE: "ZERO_BALANCE", + LOW_BALANCE: "LOW_BALANCE", + BLOCK_EXECUTION_FAILED: "BLOCK_EXECUTION_FAILED", + CONTINUOUS_AGENT_ERROR: "CONTINUOUS_AGENT_ERROR", + DAILY_SUMMARY: "DAILY_SUMMARY", + WEEKLY_SUMMARY: "WEEKLY_SUMMARY", + MONTHLY_SUMMARY: "MONTHLY_SUMMARY", + REFUND_REQUEST: "REFUND_REQUEST", + REFUND_PROCESSED: "REFUND_PROCESSED", +} as const; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/oAuth2Credentials.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/oAuth2Credentials.ts new file mode 100644 index 0000000000..bb722084fd --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/oAuth2Credentials.ts @@ -0,0 +1,27 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { OAuth2CredentialsTitle } from "./oAuth2CredentialsTitle"; +import type { OAuth2CredentialsUsername } from "./oAuth2CredentialsUsername"; +import type { OAuth2CredentialsAccessTokenExpiresAt } from "./oAuth2CredentialsAccessTokenExpiresAt"; +import type { OAuth2CredentialsRefreshToken } from "./oAuth2CredentialsRefreshToken"; +import type { OAuth2CredentialsRefreshTokenExpiresAt } from "./oAuth2CredentialsRefreshTokenExpiresAt"; +import type { OAuth2CredentialsMetadata } from "./oAuth2CredentialsMetadata"; + +export interface OAuth2Credentials { + id?: string; + provider: string; + title?: OAuth2CredentialsTitle; + type?: "oauth2"; + username?: OAuth2CredentialsUsername; + access_token: string; + access_token_expires_at?: OAuth2CredentialsAccessTokenExpiresAt; + refresh_token?: OAuth2CredentialsRefreshToken; + refresh_token_expires_at?: OAuth2CredentialsRefreshTokenExpiresAt; + scopes: string[]; + metadata?: OAuth2CredentialsMetadata; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/oAuth2CredentialsAccessTokenExpiresAt.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/oAuth2CredentialsAccessTokenExpiresAt.ts new file mode 100644 index 0000000000..d4b56c864a --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/oAuth2CredentialsAccessTokenExpiresAt.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type OAuth2CredentialsAccessTokenExpiresAt = number | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/oAuth2CredentialsMetadata.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/oAuth2CredentialsMetadata.ts new file mode 100644 index 0000000000..c9dda9c940 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/oAuth2CredentialsMetadata.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type OAuth2CredentialsMetadata = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/oAuth2CredentialsRefreshToken.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/oAuth2CredentialsRefreshToken.ts new file mode 100644 index 0000000000..eafe163e24 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/oAuth2CredentialsRefreshToken.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type OAuth2CredentialsRefreshToken = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/oAuth2CredentialsRefreshTokenExpiresAt.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/oAuth2CredentialsRefreshTokenExpiresAt.ts new file mode 100644 index 0000000000..5587d031e6 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/oAuth2CredentialsRefreshTokenExpiresAt.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type OAuth2CredentialsRefreshTokenExpiresAt = number | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/oAuth2CredentialsTitle.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/oAuth2CredentialsTitle.ts new file mode 100644 index 0000000000..d7c1351893 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/oAuth2CredentialsTitle.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type OAuth2CredentialsTitle = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/oAuth2CredentialsUsername.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/oAuth2CredentialsUsername.ts new file mode 100644 index 0000000000..653dd0bbea --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/oAuth2CredentialsUsername.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type OAuth2CredentialsUsername = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/onboardingStep.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/onboardingStep.ts new file mode 100644 index 0000000000..877b52ab8f --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/onboardingStep.ts @@ -0,0 +1,29 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type OnboardingStep = + (typeof OnboardingStep)[keyof typeof OnboardingStep]; + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export const OnboardingStep = { + WELCOME: "WELCOME", + USAGE_REASON: "USAGE_REASON", + INTEGRATIONS: "INTEGRATIONS", + AGENT_CHOICE: "AGENT_CHOICE", + AGENT_NEW_RUN: "AGENT_NEW_RUN", + AGENT_INPUT: "AGENT_INPUT", + CONGRATS: "CONGRATS", + GET_RESULTS: "GET_RESULTS", + RUN_AGENTS: "RUN_AGENTS", + MARKETPLACE_VISIT: "MARKETPLACE_VISIT", + MARKETPLACE_ADD_AGENT: "MARKETPLACE_ADD_AGENT", + MARKETPLACE_RUN_AGENT: "MARKETPLACE_RUN_AGENT", + BUILDER_OPEN: "BUILDER_OPEN", + BUILDER_SAVE_AGENT: "BUILDER_SAVE_AGENT", + BUILDER_RUN_AGENT: "BUILDER_RUN_AGENT", +} as const; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/pagination.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/pagination.ts new file mode 100644 index 0000000000..7590f225f8 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/pagination.ts @@ -0,0 +1,18 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export interface Pagination { + /** Total number of items. */ + total_items: number; + /** Total number of pages. */ + total_pages: number; + /** Current_page page number. */ + current_page: number; + /** Number of items per page. */ + page_size: number; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postV1CreateCredentials201.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postV1CreateCredentials201.ts new file mode 100644 index 0000000000..95ff25b5a1 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postV1CreateCredentials201.ts @@ -0,0 +1,17 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { OAuth2Credentials } from "./oAuth2Credentials"; +import type { APIKeyCredentials } from "./aPIKeyCredentials"; +import type { UserPasswordCredentials } from "./userPasswordCredentials"; +import type { HostScopedCredentialsOutput } from "./hostScopedCredentialsOutput"; + +export type PostV1CreateCredentials201 = + | OAuth2Credentials + | APIKeyCredentials + | UserPasswordCredentials + | HostScopedCredentialsOutput; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postV1CreateCredentialsBody.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postV1CreateCredentialsBody.ts new file mode 100644 index 0000000000..ec8f114071 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postV1CreateCredentialsBody.ts @@ -0,0 +1,17 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { OAuth2Credentials } from "./oAuth2Credentials"; +import type { APIKeyCredentials } from "./aPIKeyCredentials"; +import type { UserPasswordCredentials } from "./userPasswordCredentials"; +import type { HostScopedCredentialsInput } from "./hostScopedCredentialsInput"; + +export type PostV1CreateCredentialsBody = + | OAuth2Credentials + | APIKeyCredentials + | UserPasswordCredentials + | HostScopedCredentialsInput; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postV1ExecuteGraphAgentParams.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postV1ExecuteGraphAgentParams.ts new file mode 100644 index 0000000000..72c8020c5e --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postV1ExecuteGraphAgentParams.ts @@ -0,0 +1,11 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type PostV1ExecuteGraphAgentParams = { + preset_id?: string | null; +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postV1ExecuteGraphBlock200.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postV1ExecuteGraphBlock200.ts new file mode 100644 index 0000000000..3bf2112f45 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postV1ExecuteGraphBlock200.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type PostV1ExecuteGraphBlock200 = { [key: string]: unknown[] }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postV1ExecuteGraphBlockBody.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postV1ExecuteGraphBlockBody.ts new file mode 100644 index 0000000000..dd2a65d662 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postV1ExecuteGraphBlockBody.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type PostV1ExecuteGraphBlockBody = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postV1HandlePostmarkEmailWebhooksBody.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postV1HandlePostmarkEmailWebhooksBody.ts new file mode 100644 index 0000000000..699f5aa124 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postV1HandlePostmarkEmailWebhooksBody.ts @@ -0,0 +1,21 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { PostmarkDeliveryWebhook } from "./postmarkDeliveryWebhook"; +import type { PostmarkBounceWebhook } from "./postmarkBounceWebhook"; +import type { PostmarkSpamComplaintWebhook } from "./postmarkSpamComplaintWebhook"; +import type { PostmarkOpenWebhook } from "./postmarkOpenWebhook"; +import type { PostmarkClickWebhook } from "./postmarkClickWebhook"; +import type { PostmarkSubscriptionChangeWebhook } from "./postmarkSubscriptionChangeWebhook"; + +export type PostV1HandlePostmarkEmailWebhooksBody = + | PostmarkDeliveryWebhook + | PostmarkBounceWebhook + | PostmarkSpamComplaintWebhook + | PostmarkOpenWebhook + | PostmarkClickWebhook + | PostmarkSubscriptionChangeWebhook; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postV1OneClickEmailUnsubscribeParams.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postV1OneClickEmailUnsubscribeParams.ts new file mode 100644 index 0000000000..c577d928be --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postV1OneClickEmailUnsubscribeParams.ts @@ -0,0 +1,11 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type PostV1OneClickEmailUnsubscribeParams = { + token: string; +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postV1RefundCreditTransactionBody.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postV1RefundCreditTransactionBody.ts new file mode 100644 index 0000000000..32b2fdb83e --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postV1RefundCreditTransactionBody.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type PostV1RefundCreditTransactionBody = { [key: string]: string }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postV1UpdateUserEmail200.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postV1UpdateUserEmail200.ts new file mode 100644 index 0000000000..02089a2200 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postV1UpdateUserEmail200.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type PostV1UpdateUserEmail200 = { [key: string]: string }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postV2CreateANewPresetBody.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postV2CreateANewPresetBody.ts new file mode 100644 index 0000000000..36b8114b61 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postV2CreateANewPresetBody.ts @@ -0,0 +1,13 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { LibraryAgentPresetCreatable } from "./libraryAgentPresetCreatable"; +import type { LibraryAgentPresetCreatableFromGraphExecution } from "./libraryAgentPresetCreatableFromGraphExecution"; + +export type PostV2CreateANewPresetBody = + | LibraryAgentPresetCreatable + | LibraryAgentPresetCreatableFromGraphExecution; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postV2ExecuteAPreset200.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postV2ExecuteAPreset200.ts new file mode 100644 index 0000000000..fc0248fbe3 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postV2ExecuteAPreset200.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type PostV2ExecuteAPreset200 = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postV2GenerateSubmissionImageParams.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postV2GenerateSubmissionImageParams.ts new file mode 100644 index 0000000000..952b6e684f --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postV2GenerateSubmissionImageParams.ts @@ -0,0 +1,11 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type PostV2GenerateSubmissionImageParams = { + agent_id: string; +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkBounceEnum.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkBounceEnum.ts new file mode 100644 index 0000000000..b9f7d70c36 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkBounceEnum.ts @@ -0,0 +1,36 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type PostmarkBounceEnum = + (typeof PostmarkBounceEnum)[keyof typeof PostmarkBounceEnum]; + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export const PostmarkBounceEnum = { + NUMBER_1: 1, + NUMBER_2: 2, + NUMBER_16: 16, + NUMBER_32: 32, + NUMBER_64: 64, + NUMBER_128: 128, + NUMBER_256: 256, + NUMBER_512: 512, + NUMBER_1024: 1024, + NUMBER_2048: 2048, + NUMBER_4096: 4096, + NUMBER_8192: 8192, + NUMBER_16384: 16384, + NUMBER_100000: 100000, + NUMBER_100001: 100001, + NUMBER_100002: 100002, + NUMBER_100003: 100003, + NUMBER_100006: 100006, + NUMBER_100007: 100007, + NUMBER_100008: 100008, + NUMBER_100009: 100009, + NUMBER_100010: 100010, +} as const; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkBounceWebhook.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkBounceWebhook.ts new file mode 100644 index 0000000000..8705792b6c --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkBounceWebhook.ts @@ -0,0 +1,32 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { PostmarkBounceEnum } from "./postmarkBounceEnum"; +import type { PostmarkBounceWebhookMetadata } from "./postmarkBounceWebhookMetadata"; + +export interface PostmarkBounceWebhook { + RecordType?: "Bounce"; + ID: number; + Type: string; + TypeCode: PostmarkBounceEnum; + Tag: string; + MessageID: string; + Details: string; + Email: string; + From: string; + BouncedAt: string; + Inactive: boolean; + DumpAvailable: boolean; + CanActivate: boolean; + Subject: string; + ServerID: number; + MessageStream: string; + Content: string; + Name: string; + Description: string; + Metadata: PostmarkBounceWebhookMetadata; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkBounceWebhookMetadata.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkBounceWebhookMetadata.ts new file mode 100644 index 0000000000..b44b9760bd --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkBounceWebhookMetadata.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type PostmarkBounceWebhookMetadata = { [key: string]: string }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkClickWebhook.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkClickWebhook.ts new file mode 100644 index 0000000000..69923fa136 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkClickWebhook.ts @@ -0,0 +1,28 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { PostmarkClickWebhookMetadata } from "./postmarkClickWebhookMetadata"; +import type { PostmarkClickWebhookOS } from "./postmarkClickWebhookOS"; +import type { PostmarkClickWebhookClient } from "./postmarkClickWebhookClient"; +import type { PostmarkClickWebhookGeo } from "./postmarkClickWebhookGeo"; + +export interface PostmarkClickWebhook { + RecordType?: "Click"; + MessageStream: string; + Metadata: PostmarkClickWebhookMetadata; + Recipient: string; + MessageID: string; + ReceivedAt: string; + Platform: string; + ClickLocation: string; + OriginalLink: string; + Tag: string; + UserAgent: string; + OS: PostmarkClickWebhookOS; + Client: PostmarkClickWebhookClient; + Geo: PostmarkClickWebhookGeo; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkClickWebhookClient.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkClickWebhookClient.ts new file mode 100644 index 0000000000..1153e6ab27 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkClickWebhookClient.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type PostmarkClickWebhookClient = { [key: string]: string }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkClickWebhookGeo.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkClickWebhookGeo.ts new file mode 100644 index 0000000000..3236c128ef --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkClickWebhookGeo.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type PostmarkClickWebhookGeo = { [key: string]: string }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkClickWebhookMetadata.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkClickWebhookMetadata.ts new file mode 100644 index 0000000000..1c105912b2 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkClickWebhookMetadata.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type PostmarkClickWebhookMetadata = { [key: string]: string }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkClickWebhookOS.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkClickWebhookOS.ts new file mode 100644 index 0000000000..b61d05a9aa --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkClickWebhookOS.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type PostmarkClickWebhookOS = { [key: string]: string }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkDeliveryWebhook.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkDeliveryWebhook.ts new file mode 100644 index 0000000000..f4fd9be417 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkDeliveryWebhook.ts @@ -0,0 +1,20 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { PostmarkDeliveryWebhookMetadata } from "./postmarkDeliveryWebhookMetadata"; + +export interface PostmarkDeliveryWebhook { + RecordType?: "Delivery"; + ServerID: number; + MessageStream: string; + MessageID: string; + Recipient: string; + Tag: string; + DeliveredAt: string; + Details: string; + Metadata: PostmarkDeliveryWebhookMetadata; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkDeliveryWebhookMetadata.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkDeliveryWebhookMetadata.ts new file mode 100644 index 0000000000..ec054608e8 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkDeliveryWebhookMetadata.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type PostmarkDeliveryWebhookMetadata = { [key: string]: string }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkOpenWebhook.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkOpenWebhook.ts new file mode 100644 index 0000000000..c31c737c19 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkOpenWebhook.ts @@ -0,0 +1,28 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { PostmarkOpenWebhookMetadata } from "./postmarkOpenWebhookMetadata"; +import type { PostmarkOpenWebhookOS } from "./postmarkOpenWebhookOS"; +import type { PostmarkOpenWebhookClient } from "./postmarkOpenWebhookClient"; +import type { PostmarkOpenWebhookGeo } from "./postmarkOpenWebhookGeo"; + +export interface PostmarkOpenWebhook { + RecordType?: "Open"; + MessageStream: string; + Metadata: PostmarkOpenWebhookMetadata; + FirstOpen: boolean; + Recipient: string; + MessageID: string; + ReceivedAt: string; + Platform: string; + ReadSeconds: number; + Tag: string; + UserAgent: string; + OS: PostmarkOpenWebhookOS; + Client: PostmarkOpenWebhookClient; + Geo: PostmarkOpenWebhookGeo; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkOpenWebhookClient.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkOpenWebhookClient.ts new file mode 100644 index 0000000000..92bb4ca083 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkOpenWebhookClient.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type PostmarkOpenWebhookClient = { [key: string]: string }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkOpenWebhookGeo.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkOpenWebhookGeo.ts new file mode 100644 index 0000000000..f1523d16c2 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkOpenWebhookGeo.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type PostmarkOpenWebhookGeo = { [key: string]: string }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkOpenWebhookMetadata.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkOpenWebhookMetadata.ts new file mode 100644 index 0000000000..1fb7dcf546 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkOpenWebhookMetadata.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type PostmarkOpenWebhookMetadata = { [key: string]: string }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkOpenWebhookOS.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkOpenWebhookOS.ts new file mode 100644 index 0000000000..003197ae47 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkOpenWebhookOS.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type PostmarkOpenWebhookOS = { [key: string]: string }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkSpamComplaintWebhook.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkSpamComplaintWebhook.ts new file mode 100644 index 0000000000..6752559fd0 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkSpamComplaintWebhook.ts @@ -0,0 +1,31 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { PostmarkSpamComplaintWebhookMetadata } from "./postmarkSpamComplaintWebhookMetadata"; + +export interface PostmarkSpamComplaintWebhook { + RecordType?: "SpamComplaint"; + ID: number; + Type: string; + TypeCode: number; + Tag: string; + MessageID: string; + Details: string; + Email: string; + From: string; + BouncedAt: string; + Inactive: boolean; + DumpAvailable: boolean; + CanActivate: boolean; + Subject: string; + ServerID: number; + MessageStream: string; + Content: string; + Name: string; + Description: string; + Metadata: PostmarkSpamComplaintWebhookMetadata; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkSpamComplaintWebhookMetadata.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkSpamComplaintWebhookMetadata.ts new file mode 100644 index 0000000000..9f2c41f27e --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkSpamComplaintWebhookMetadata.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type PostmarkSpamComplaintWebhookMetadata = { [key: string]: string }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkSubscriptionChangeWebhook.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkSubscriptionChangeWebhook.ts new file mode 100644 index 0000000000..cf8c1c540b --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkSubscriptionChangeWebhook.ts @@ -0,0 +1,22 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { PostmarkSubscriptionChangeWebhookMetadata } from "./postmarkSubscriptionChangeWebhookMetadata"; + +export interface PostmarkSubscriptionChangeWebhook { + RecordType?: "SubscriptionChange"; + MessageID: string; + ServerID: number; + MessageStream: string; + ChangedAt: string; + Recipient: string; + Origin: string; + SuppressSending: boolean; + SuppressionReason: string; + Tag: string; + Metadata: PostmarkSubscriptionChangeWebhookMetadata; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkSubscriptionChangeWebhookMetadata.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkSubscriptionChangeWebhookMetadata.ts new file mode 100644 index 0000000000..e534c685c4 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/postmarkSubscriptionChangeWebhookMetadata.ts @@ -0,0 +1,11 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type PostmarkSubscriptionChangeWebhookMetadata = { + [key: string]: string; +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/profile.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/profile.ts new file mode 100644 index 0000000000..072e9ae270 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/profile.ts @@ -0,0 +1,16 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export interface Profile { + name: string; + username: string; + description: string; + links: string[]; + avatar_url: string; + is_featured?: boolean; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/profileDetails.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/profileDetails.ts new file mode 100644 index 0000000000..e92dbab59d --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/profileDetails.ts @@ -0,0 +1,16 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { ProfileDetailsAvatarUrl } from "./profileDetailsAvatarUrl"; + +export interface ProfileDetails { + name: string; + username: string; + description: string; + links: string[]; + avatar_url?: ProfileDetailsAvatarUrl; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/profileDetailsAvatarUrl.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/profileDetailsAvatarUrl.ts new file mode 100644 index 0000000000..ff303842e5 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/profileDetailsAvatarUrl.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type ProfileDetailsAvatarUrl = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/providerName.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/providerName.ts new file mode 100644 index 0000000000..198bb7a5bf --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/providerName.ts @@ -0,0 +1,53 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type ProviderName = (typeof ProviderName)[keyof typeof ProviderName]; + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export const ProviderName = { + aiml_api: "aiml_api", + anthropic: "anthropic", + apollo: "apollo", + compass: "compass", + discord: "discord", + d_id: "d_id", + e2b: "e2b", + exa: "exa", + fal: "fal", + generic_webhook: "generic_webhook", + github: "github", + google: "google", + google_maps: "google_maps", + groq: "groq", + http: "http", + hubspot: "hubspot", + ideogram: "ideogram", + jina: "jina", + linear: "linear", + llama_api: "llama_api", + medium: "medium", + mem0: "mem0", + notion: "notion", + nvidia: "nvidia", + ollama: "ollama", + openai: "openai", + openweathermap: "openweathermap", + open_router: "open_router", + pinecone: "pinecone", + reddit: "reddit", + replicate: "replicate", + revid: "revid", + screenshotone: "screenshotone", + slant3d: "slant3d", + smartlead: "smartlead", + smtp: "smtp", + twitter: "twitter", + todoist: "todoist", + unreal_speech: "unreal_speech", + zerobounce: "zerobounce", +} as const; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/refundRequest.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/refundRequest.ts new file mode 100644 index 0000000000..1b658170af --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/refundRequest.ts @@ -0,0 +1,20 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { RefundRequestResult } from "./refundRequestResult"; + +export interface RefundRequest { + id: string; + user_id: string; + transaction_key: string; + amount: number; + reason: string; + result?: RefundRequestResult; + status: string; + created_at: string; + updated_at: string; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/refundRequestResult.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/refundRequestResult.ts new file mode 100644 index 0000000000..165c4832ef --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/refundRequestResult.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type RefundRequestResult = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/requestTopUp.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/requestTopUp.ts new file mode 100644 index 0000000000..12d9a4b9fd --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/requestTopUp.ts @@ -0,0 +1,11 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export interface RequestTopUp { + credit_amount: number; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/reviewSubmissionRequest.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/reviewSubmissionRequest.ts new file mode 100644 index 0000000000..b987faedcd --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/reviewSubmissionRequest.ts @@ -0,0 +1,15 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { ReviewSubmissionRequestInternalComments } from "./reviewSubmissionRequestInternalComments"; + +export interface ReviewSubmissionRequest { + store_listing_version_id: string; + is_approved: boolean; + comments: string; + internal_comments?: ReviewSubmissionRequestInternalComments; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/reviewSubmissionRequestInternalComments.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/reviewSubmissionRequestInternalComments.ts new file mode 100644 index 0000000000..d3969adb0e --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/reviewSubmissionRequestInternalComments.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type ReviewSubmissionRequestInternalComments = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/scheduleCreationRequest.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/scheduleCreationRequest.ts new file mode 100644 index 0000000000..5f530cc843 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/scheduleCreationRequest.ts @@ -0,0 +1,18 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { ScheduleCreationRequestGraphVersion } from "./scheduleCreationRequestGraphVersion"; +import type { ScheduleCreationRequestInputs } from "./scheduleCreationRequestInputs"; +import type { ScheduleCreationRequestCredentials } from "./scheduleCreationRequestCredentials"; + +export interface ScheduleCreationRequest { + graph_version?: ScheduleCreationRequestGraphVersion; + name: string; + cron: string; + inputs: ScheduleCreationRequestInputs; + credentials?: ScheduleCreationRequestCredentials; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/scheduleCreationRequestCredentials.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/scheduleCreationRequestCredentials.ts new file mode 100644 index 0000000000..c57c8604e4 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/scheduleCreationRequestCredentials.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { CredentialsMetaInput } from "./credentialsMetaInput"; + +export type ScheduleCreationRequestCredentials = { + [key: string]: CredentialsMetaInput; +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/scheduleCreationRequestGraphVersion.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/scheduleCreationRequestGraphVersion.ts new file mode 100644 index 0000000000..8049053e55 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/scheduleCreationRequestGraphVersion.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type ScheduleCreationRequestGraphVersion = number | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/scheduleCreationRequestInputs.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/scheduleCreationRequestInputs.ts new file mode 100644 index 0000000000..ce851b98f6 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/scheduleCreationRequestInputs.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type ScheduleCreationRequestInputs = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/setGraphActiveVersion.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/setGraphActiveVersion.ts new file mode 100644 index 0000000000..d057bd2cc2 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/setGraphActiveVersion.ts @@ -0,0 +1,11 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export interface SetGraphActiveVersion { + active_graph_version: number; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/stats.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/stats.ts new file mode 100644 index 0000000000..a98a0e45d7 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/stats.ts @@ -0,0 +1,28 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { StatsError } from "./statsError"; + +export interface Stats { + /** Execution cost (cents) */ + cost?: number; + /** Seconds from start to end of run */ + duration?: number; + /** CPU sec of duration */ + duration_cpu_only?: number; + /** Seconds of total node runtime */ + node_exec_time?: number; + /** CPU sec of node_exec_time */ + node_exec_time_cpu_only?: number; + /** Number of node executions */ + node_exec_count?: number; + /** Number of node errors */ + node_error_count?: number; + /** Error message if any */ + error?: StatsError; + [key: string]: unknown; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/statsError.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/statsError.ts new file mode 100644 index 0000000000..c2e0f8b5f8 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/statsError.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +/** + * Error message if any + */ +export type StatsError = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/storeAgent.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/storeAgent.ts new file mode 100644 index 0000000000..f34807629a --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/storeAgent.ts @@ -0,0 +1,19 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export interface StoreAgent { + slug: string; + agent_name: string; + agent_image: string; + creator: string; + creator_avatar: string; + sub_heading: string; + description: string; + runs: number; + rating: number; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/storeAgentDetails.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/storeAgentDetails.ts new file mode 100644 index 0000000000..3f64cc9699 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/storeAgentDetails.ts @@ -0,0 +1,27 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { StoreAgentDetailsActiveVersionId } from "./storeAgentDetailsActiveVersionId"; + +export interface StoreAgentDetails { + store_listing_version_id: string; + slug: string; + agent_name: string; + agent_video: string; + agent_image: string[]; + creator: string; + creator_avatar: string; + sub_heading: string; + description: string; + categories: string[]; + runs: number; + rating: number; + versions: string[]; + last_updated: string; + active_version_id?: StoreAgentDetailsActiveVersionId; + has_approved_version?: boolean; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/storeAgentDetailsActiveVersionId.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/storeAgentDetailsActiveVersionId.ts new file mode 100644 index 0000000000..cb8f6a67a3 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/storeAgentDetailsActiveVersionId.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type StoreAgentDetailsActiveVersionId = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/storeAgentsResponse.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/storeAgentsResponse.ts new file mode 100644 index 0000000000..aa18e6e06b --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/storeAgentsResponse.ts @@ -0,0 +1,14 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { StoreAgent } from "./storeAgent"; +import type { Pagination } from "./pagination"; + +export interface StoreAgentsResponse { + agents: StoreAgent[]; + pagination: Pagination; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/storeListingWithVersions.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/storeListingWithVersions.ts new file mode 100644 index 0000000000..c4facb7fb5 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/storeListingWithVersions.ts @@ -0,0 +1,26 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { StoreListingWithVersionsActiveVersionId } from "./storeListingWithVersionsActiveVersionId"; +import type { StoreListingWithVersionsCreatorEmail } from "./storeListingWithVersionsCreatorEmail"; +import type { StoreListingWithVersionsLatestVersion } from "./storeListingWithVersionsLatestVersion"; +import type { StoreSubmission } from "./storeSubmission"; + +/** + * A store listing with its version history + */ +export interface StoreListingWithVersions { + listing_id: string; + slug: string; + agent_id: string; + agent_version: number; + active_version_id?: StoreListingWithVersionsActiveVersionId; + has_approved_version?: boolean; + creator_email?: StoreListingWithVersionsCreatorEmail; + latest_version?: StoreListingWithVersionsLatestVersion; + versions?: StoreSubmission[]; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/storeListingWithVersionsActiveVersionId.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/storeListingWithVersionsActiveVersionId.ts new file mode 100644 index 0000000000..d497a65921 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/storeListingWithVersionsActiveVersionId.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type StoreListingWithVersionsActiveVersionId = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/storeListingWithVersionsCreatorEmail.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/storeListingWithVersionsCreatorEmail.ts new file mode 100644 index 0000000000..bb1be13a85 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/storeListingWithVersionsCreatorEmail.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type StoreListingWithVersionsCreatorEmail = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/storeListingWithVersionsLatestVersion.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/storeListingWithVersionsLatestVersion.ts new file mode 100644 index 0000000000..b5306f0a2b --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/storeListingWithVersionsLatestVersion.ts @@ -0,0 +1,10 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { StoreSubmission } from "./storeSubmission"; + +export type StoreListingWithVersionsLatestVersion = StoreSubmission | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/storeListingsWithVersionsResponse.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/storeListingsWithVersionsResponse.ts new file mode 100644 index 0000000000..0d1ac8aeb3 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/storeListingsWithVersionsResponse.ts @@ -0,0 +1,17 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { StoreListingWithVersions } from "./storeListingWithVersions"; +import type { Pagination } from "./pagination"; + +/** + * Response model for listings with version history + */ +export interface StoreListingsWithVersionsResponse { + listings: StoreListingWithVersions[]; + pagination: Pagination; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/storeReview.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/storeReview.ts new file mode 100644 index 0000000000..3a36db855b --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/storeReview.ts @@ -0,0 +1,13 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { StoreReviewComments } from "./storeReviewComments"; + +export interface StoreReview { + score: number; + comments?: StoreReviewComments; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/storeReviewComments.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/storeReviewComments.ts new file mode 100644 index 0000000000..4506670bec --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/storeReviewComments.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type StoreReviewComments = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/storeReviewCreate.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/storeReviewCreate.ts new file mode 100644 index 0000000000..936598e927 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/storeReviewCreate.ts @@ -0,0 +1,14 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { StoreReviewCreateComments } from "./storeReviewCreateComments"; + +export interface StoreReviewCreate { + store_listing_version_id: string; + score: number; + comments?: StoreReviewCreateComments; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/storeReviewCreateComments.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/storeReviewCreateComments.ts new file mode 100644 index 0000000000..64a98a6252 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/storeReviewCreateComments.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type StoreReviewCreateComments = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmission.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmission.ts new file mode 100644 index 0000000000..9bc1db6f7a --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmission.ts @@ -0,0 +1,36 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { SubmissionStatus } from "./submissionStatus"; +import type { StoreSubmissionStoreListingVersionId } from "./storeSubmissionStoreListingVersionId"; +import type { StoreSubmissionVersion } from "./storeSubmissionVersion"; +import type { StoreSubmissionReviewerId } from "./storeSubmissionReviewerId"; +import type { StoreSubmissionReviewComments } from "./storeSubmissionReviewComments"; +import type { StoreSubmissionInternalComments } from "./storeSubmissionInternalComments"; +import type { StoreSubmissionReviewedAt } from "./storeSubmissionReviewedAt"; +import type { StoreSubmissionChangesSummary } from "./storeSubmissionChangesSummary"; + +export interface StoreSubmission { + agent_id: string; + agent_version: number; + name: string; + sub_heading: string; + slug: string; + description: string; + image_urls: string[]; + date_submitted: string; + status: SubmissionStatus; + runs: number; + rating: number; + store_listing_version_id?: StoreSubmissionStoreListingVersionId; + version?: StoreSubmissionVersion; + reviewer_id?: StoreSubmissionReviewerId; + review_comments?: StoreSubmissionReviewComments; + internal_comments?: StoreSubmissionInternalComments; + reviewed_at?: StoreSubmissionReviewedAt; + changes_summary?: StoreSubmissionChangesSummary; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionChangesSummary.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionChangesSummary.ts new file mode 100644 index 0000000000..e093e8b4e9 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionChangesSummary.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type StoreSubmissionChangesSummary = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionInternalComments.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionInternalComments.ts new file mode 100644 index 0000000000..ce3c6c45b1 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionInternalComments.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type StoreSubmissionInternalComments = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionRequest.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionRequest.ts new file mode 100644 index 0000000000..20cf007b2a --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionRequest.ts @@ -0,0 +1,22 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { StoreSubmissionRequestVideoUrl } from "./storeSubmissionRequestVideoUrl"; +import type { StoreSubmissionRequestChangesSummary } from "./storeSubmissionRequestChangesSummary"; + +export interface StoreSubmissionRequest { + agent_id: string; + agent_version: number; + slug: string; + name: string; + sub_heading: string; + video_url?: StoreSubmissionRequestVideoUrl; + image_urls?: string[]; + description?: string; + categories?: string[]; + changes_summary?: StoreSubmissionRequestChangesSummary; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionRequestChangesSummary.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionRequestChangesSummary.ts new file mode 100644 index 0000000000..1078c1f1cd --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionRequestChangesSummary.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type StoreSubmissionRequestChangesSummary = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionRequestVideoUrl.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionRequestVideoUrl.ts new file mode 100644 index 0000000000..7ce72c00ff --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionRequestVideoUrl.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type StoreSubmissionRequestVideoUrl = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionReviewComments.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionReviewComments.ts new file mode 100644 index 0000000000..13ae778840 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionReviewComments.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type StoreSubmissionReviewComments = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionReviewedAt.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionReviewedAt.ts new file mode 100644 index 0000000000..5e12ef999c --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionReviewedAt.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type StoreSubmissionReviewedAt = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionReviewerId.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionReviewerId.ts new file mode 100644 index 0000000000..a89c18be9a --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionReviewerId.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type StoreSubmissionReviewerId = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionStoreListingVersionId.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionStoreListingVersionId.ts new file mode 100644 index 0000000000..87be23bc93 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionStoreListingVersionId.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type StoreSubmissionStoreListingVersionId = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionVersion.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionVersion.ts new file mode 100644 index 0000000000..7a9623e1bc --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionVersion.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type StoreSubmissionVersion = number | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionsResponse.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionsResponse.ts new file mode 100644 index 0000000000..0995244b9c --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/storeSubmissionsResponse.ts @@ -0,0 +1,14 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { StoreSubmission } from "./storeSubmission"; +import type { Pagination } from "./pagination"; + +export interface StoreSubmissionsResponse { + submissions: StoreSubmission[]; + pagination: Pagination; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/submissionStatus.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/submissionStatus.ts new file mode 100644 index 0000000000..fd3a42fea5 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/submissionStatus.ts @@ -0,0 +1,18 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type SubmissionStatus = + (typeof SubmissionStatus)[keyof typeof SubmissionStatus]; + +// eslint-disable-next-line @typescript-eslint/no-redeclare +export const SubmissionStatus = { + DRAFT: "DRAFT", + PENDING: "PENDING", + APPROVED: "APPROVED", + REJECTED: "REJECTED", +} as const; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/transactionHistory.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/transactionHistory.ts new file mode 100644 index 0000000000..20303cbaa1 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/transactionHistory.ts @@ -0,0 +1,14 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { UserTransaction } from "./userTransaction"; +import type { TransactionHistoryNextTransactionTime } from "./transactionHistoryNextTransactionTime"; + +export interface TransactionHistory { + transactions: UserTransaction[]; + next_transaction_time: TransactionHistoryNextTransactionTime; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/transactionHistoryNextTransactionTime.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/transactionHistoryNextTransactionTime.ts new file mode 100644 index 0000000000..e18d217480 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/transactionHistoryNextTransactionTime.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type TransactionHistoryNextTransactionTime = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/triggeredPresetSetupParams.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/triggeredPresetSetupParams.ts new file mode 100644 index 0000000000..005fbf1042 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/triggeredPresetSetupParams.ts @@ -0,0 +1,16 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { TriggeredPresetSetupParamsTriggerConfig } from "./triggeredPresetSetupParamsTriggerConfig"; +import type { TriggeredPresetSetupParamsAgentCredentials } from "./triggeredPresetSetupParamsAgentCredentials"; + +export interface TriggeredPresetSetupParams { + name: string; + description?: string; + trigger_config: TriggeredPresetSetupParamsTriggerConfig; + agent_credentials?: TriggeredPresetSetupParamsAgentCredentials; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/triggeredPresetSetupParamsAgentCredentials.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/triggeredPresetSetupParamsAgentCredentials.ts new file mode 100644 index 0000000000..03f52d444f --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/triggeredPresetSetupParamsAgentCredentials.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { CredentialsMetaInput } from "./credentialsMetaInput"; + +export type TriggeredPresetSetupParamsAgentCredentials = { + [key: string]: CredentialsMetaInput; +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/triggeredPresetSetupParamsTriggerConfig.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/triggeredPresetSetupParamsTriggerConfig.ts new file mode 100644 index 0000000000..3d515dfd09 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/triggeredPresetSetupParamsTriggerConfig.ts @@ -0,0 +1,11 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type TriggeredPresetSetupParamsTriggerConfig = { + [key: string]: unknown; +}; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/turnstileVerifyRequest.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/turnstileVerifyRequest.ts new file mode 100644 index 0000000000..75bdc2f91e --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/turnstileVerifyRequest.ts @@ -0,0 +1,18 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { TurnstileVerifyRequestAction } from "./turnstileVerifyRequestAction"; + +/** + * Request model for verifying a Turnstile token. + */ +export interface TurnstileVerifyRequest { + /** The Turnstile token to verify */ + token: string; + /** The action that the user is attempting to perform */ + action?: TurnstileVerifyRequestAction; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/turnstileVerifyRequestAction.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/turnstileVerifyRequestAction.ts new file mode 100644 index 0000000000..9506b2993b --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/turnstileVerifyRequestAction.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +/** + * The action that the user is attempting to perform + */ +export type TurnstileVerifyRequestAction = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/turnstileVerifyResponse.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/turnstileVerifyResponse.ts new file mode 100644 index 0000000000..6ee4d8966d --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/turnstileVerifyResponse.ts @@ -0,0 +1,27 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { TurnstileVerifyResponseError } from "./turnstileVerifyResponseError"; +import type { TurnstileVerifyResponseChallengeTimestamp } from "./turnstileVerifyResponseChallengeTimestamp"; +import type { TurnstileVerifyResponseHostname } from "./turnstileVerifyResponseHostname"; +import type { TurnstileVerifyResponseAction } from "./turnstileVerifyResponseAction"; + +/** + * Response model for the Turnstile verification endpoint. + */ +export interface TurnstileVerifyResponse { + /** Whether the token verification was successful */ + success: boolean; + /** Error message if verification failed */ + error?: TurnstileVerifyResponseError; + /** Timestamp of the challenge (ISO format) */ + challenge_timestamp?: TurnstileVerifyResponseChallengeTimestamp; + /** Hostname of the site where the challenge was solved */ + hostname?: TurnstileVerifyResponseHostname; + /** The action associated with this verification */ + action?: TurnstileVerifyResponseAction; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/turnstileVerifyResponseAction.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/turnstileVerifyResponseAction.ts new file mode 100644 index 0000000000..254fb74a27 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/turnstileVerifyResponseAction.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +/** + * The action associated with this verification + */ +export type TurnstileVerifyResponseAction = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/turnstileVerifyResponseChallengeTimestamp.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/turnstileVerifyResponseChallengeTimestamp.ts new file mode 100644 index 0000000000..b0a4d5dbfc --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/turnstileVerifyResponseChallengeTimestamp.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +/** + * Timestamp of the challenge (ISO format) + */ +export type TurnstileVerifyResponseChallengeTimestamp = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/turnstileVerifyResponseError.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/turnstileVerifyResponseError.ts new file mode 100644 index 0000000000..b9a3d06271 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/turnstileVerifyResponseError.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +/** + * Error message if verification failed + */ +export type TurnstileVerifyResponseError = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/turnstileVerifyResponseHostname.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/turnstileVerifyResponseHostname.ts new file mode 100644 index 0000000000..f293b4baec --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/turnstileVerifyResponseHostname.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +/** + * Hostname of the site where the challenge was solved + */ +export type TurnstileVerifyResponseHostname = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/updatePermissionsRequest.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/updatePermissionsRequest.ts new file mode 100644 index 0000000000..add6cd2895 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/updatePermissionsRequest.ts @@ -0,0 +1,12 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { APIKeyPermission } from "./aPIKeyPermission"; + +export interface UpdatePermissionsRequest { + permissions: APIKeyPermission[]; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/userHistoryResponse.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/userHistoryResponse.ts new file mode 100644 index 0000000000..5cbfe74cf6 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/userHistoryResponse.ts @@ -0,0 +1,17 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { UserTransaction } from "./userTransaction"; +import type { Pagination } from "./pagination"; + +/** + * Response model for listings with version history + */ +export interface UserHistoryResponse { + history: UserTransaction[]; + pagination: Pagination; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdate.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdate.ts new file mode 100644 index 0000000000..4772393d30 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdate.ts @@ -0,0 +1,30 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { UserOnboardingUpdateCompletedSteps } from "./userOnboardingUpdateCompletedSteps"; +import type { UserOnboardingUpdateNotificationDot } from "./userOnboardingUpdateNotificationDot"; +import type { UserOnboardingUpdateNotified } from "./userOnboardingUpdateNotified"; +import type { UserOnboardingUpdateUsageReason } from "./userOnboardingUpdateUsageReason"; +import type { UserOnboardingUpdateIntegrations } from "./userOnboardingUpdateIntegrations"; +import type { UserOnboardingUpdateOtherIntegrations } from "./userOnboardingUpdateOtherIntegrations"; +import type { UserOnboardingUpdateSelectedStoreListingVersionId } from "./userOnboardingUpdateSelectedStoreListingVersionId"; +import type { UserOnboardingUpdateAgentInput } from "./userOnboardingUpdateAgentInput"; +import type { UserOnboardingUpdateOnboardingAgentExecutionId } from "./userOnboardingUpdateOnboardingAgentExecutionId"; +import type { UserOnboardingUpdateAgentRuns } from "./userOnboardingUpdateAgentRuns"; + +export interface UserOnboardingUpdate { + completedSteps?: UserOnboardingUpdateCompletedSteps; + notificationDot?: UserOnboardingUpdateNotificationDot; + notified?: UserOnboardingUpdateNotified; + usageReason?: UserOnboardingUpdateUsageReason; + integrations?: UserOnboardingUpdateIntegrations; + otherIntegrations?: UserOnboardingUpdateOtherIntegrations; + selectedStoreListingVersionId?: UserOnboardingUpdateSelectedStoreListingVersionId; + agentInput?: UserOnboardingUpdateAgentInput; + onboardingAgentExecutionId?: UserOnboardingUpdateOnboardingAgentExecutionId; + agentRuns?: UserOnboardingUpdateAgentRuns; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateAgentInput.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateAgentInput.ts new file mode 100644 index 0000000000..7aff087768 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateAgentInput.ts @@ -0,0 +1,11 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { UserOnboardingUpdateAgentInputAnyOf } from "./userOnboardingUpdateAgentInputAnyOf"; + +export type UserOnboardingUpdateAgentInput = + UserOnboardingUpdateAgentInputAnyOf | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateAgentInputAnyOf.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateAgentInputAnyOf.ts new file mode 100644 index 0000000000..e80858c009 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateAgentInputAnyOf.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type UserOnboardingUpdateAgentInputAnyOf = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateAgentRuns.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateAgentRuns.ts new file mode 100644 index 0000000000..86b63b2d36 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateAgentRuns.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type UserOnboardingUpdateAgentRuns = number | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateCompletedSteps.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateCompletedSteps.ts new file mode 100644 index 0000000000..587c501dfe --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateCompletedSteps.ts @@ -0,0 +1,10 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { OnboardingStep } from "./onboardingStep"; + +export type UserOnboardingUpdateCompletedSteps = OnboardingStep[] | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateIntegrations.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateIntegrations.ts new file mode 100644 index 0000000000..2b9d88820a --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateIntegrations.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type UserOnboardingUpdateIntegrations = string[] | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateNotificationDot.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateNotificationDot.ts new file mode 100644 index 0000000000..8e3b3f5715 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateNotificationDot.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type UserOnboardingUpdateNotificationDot = boolean | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateNotified.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateNotified.ts new file mode 100644 index 0000000000..5b6bfe7f2f --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateNotified.ts @@ -0,0 +1,10 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { OnboardingStep } from "./onboardingStep"; + +export type UserOnboardingUpdateNotified = OnboardingStep[] | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateOnboardingAgentExecutionId.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateOnboardingAgentExecutionId.ts new file mode 100644 index 0000000000..22a9287ef6 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateOnboardingAgentExecutionId.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type UserOnboardingUpdateOnboardingAgentExecutionId = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateOtherIntegrations.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateOtherIntegrations.ts new file mode 100644 index 0000000000..646db4c056 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateOtherIntegrations.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type UserOnboardingUpdateOtherIntegrations = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateSelectedStoreListingVersionId.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateSelectedStoreListingVersionId.ts new file mode 100644 index 0000000000..a4e565d2dd --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateSelectedStoreListingVersionId.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type UserOnboardingUpdateSelectedStoreListingVersionId = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateUsageReason.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateUsageReason.ts new file mode 100644 index 0000000000..27963ed9b4 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/userOnboardingUpdateUsageReason.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type UserOnboardingUpdateUsageReason = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/userPasswordCredentials.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/userPasswordCredentials.ts new file mode 100644 index 0000000000..b1738849cb --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/userPasswordCredentials.ts @@ -0,0 +1,17 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { UserPasswordCredentialsTitle } from "./userPasswordCredentialsTitle"; + +export interface UserPasswordCredentials { + id?: string; + provider: string; + title?: UserPasswordCredentialsTitle; + type?: "user_password"; + username: string; + password: string; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/userPasswordCredentialsTitle.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/userPasswordCredentialsTitle.ts new file mode 100644 index 0000000000..c23ec7506a --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/userPasswordCredentialsTitle.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type UserPasswordCredentialsTitle = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/userTransaction.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/userTransaction.ts new file mode 100644 index 0000000000..a43062920e --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/userTransaction.ts @@ -0,0 +1,34 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { CreditTransactionType } from "./creditTransactionType"; +import type { UserTransactionDescription } from "./userTransactionDescription"; +import type { UserTransactionUsageGraphId } from "./userTransactionUsageGraphId"; +import type { UserTransactionUsageExecutionId } from "./userTransactionUsageExecutionId"; +import type { UserTransactionUserEmail } from "./userTransactionUserEmail"; +import type { UserTransactionReason } from "./userTransactionReason"; +import type { UserTransactionAdminEmail } from "./userTransactionAdminEmail"; +import type { UserTransactionExtraData } from "./userTransactionExtraData"; + +export interface UserTransaction { + transaction_key?: string; + transaction_time?: string; + transaction_type?: CreditTransactionType; + amount?: number; + running_balance?: number; + current_balance?: number; + description?: UserTransactionDescription; + usage_graph_id?: UserTransactionUsageGraphId; + usage_execution_id?: UserTransactionUsageExecutionId; + usage_node_count?: number; + usage_start_time?: string; + user_id: string; + user_email?: UserTransactionUserEmail; + reason?: UserTransactionReason; + admin_email?: UserTransactionAdminEmail; + extra_data?: UserTransactionExtraData; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/userTransactionAdminEmail.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/userTransactionAdminEmail.ts new file mode 100644 index 0000000000..6caa54836a --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/userTransactionAdminEmail.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type UserTransactionAdminEmail = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/userTransactionDescription.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/userTransactionDescription.ts new file mode 100644 index 0000000000..2e47fb2436 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/userTransactionDescription.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type UserTransactionDescription = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/userTransactionExtraData.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/userTransactionExtraData.ts new file mode 100644 index 0000000000..2d1150ae93 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/userTransactionExtraData.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type UserTransactionExtraData = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/userTransactionReason.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/userTransactionReason.ts new file mode 100644 index 0000000000..6a5dfd23d1 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/userTransactionReason.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type UserTransactionReason = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/userTransactionUsageExecutionId.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/userTransactionUsageExecutionId.ts new file mode 100644 index 0000000000..cd5675662b --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/userTransactionUsageExecutionId.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type UserTransactionUsageExecutionId = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/userTransactionUsageGraphId.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/userTransactionUsageGraphId.ts new file mode 100644 index 0000000000..57eaa4505b --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/userTransactionUsageGraphId.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type UserTransactionUsageGraphId = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/userTransactionUserEmail.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/userTransactionUserEmail.ts new file mode 100644 index 0000000000..f3fa396804 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/userTransactionUserEmail.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type UserTransactionUserEmail = string | null; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/validationError.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/validationError.ts new file mode 100644 index 0000000000..0d6b4688dd --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/validationError.ts @@ -0,0 +1,14 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { ValidationErrorLocItem } from "./validationErrorLocItem"; + +export interface ValidationError { + loc: ValidationErrorLocItem[]; + msg: string; + type: string; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/validationErrorLocItem.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/validationErrorLocItem.ts new file mode 100644 index 0000000000..28822590d3 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/validationErrorLocItem.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type ValidationErrorLocItem = string | number; diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/webhook.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/webhook.ts new file mode 100644 index 0000000000..0f48986b6a --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/webhook.ts @@ -0,0 +1,23 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ +import type { ProviderName } from "./providerName"; +import type { WebhookConfig } from "./webhookConfig"; + +export interface Webhook { + id?: string; + user_id: string; + provider: ProviderName; + credentials_id: string; + webhook_type: string; + resource: string; + events: string[]; + config?: WebhookConfig; + secret: string; + provider_webhook_id: string; + readonly url: string; +} diff --git a/autogpt_platform/frontend/src/app/api/__generated__/models/webhookConfig.ts b/autogpt_platform/frontend/src/app/api/__generated__/models/webhookConfig.ts new file mode 100644 index 0000000000..0a6619bbb0 --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/__generated__/models/webhookConfig.ts @@ -0,0 +1,9 @@ +/** + * Generated by orval v7.10.0 🍺 + * Do not edit manually. + * AutoGPT Agent Server + * This server is used to execute agents that are created by the AutoGPT system. + * OpenAPI spec version: 0.1 + */ + +export type WebhookConfig = { [key: string]: unknown }; diff --git a/autogpt_platform/frontend/src/api/mutators/custom-mutator.ts b/autogpt_platform/frontend/src/app/api/mutators/custom-mutator.ts similarity index 72% rename from autogpt_platform/frontend/src/api/mutators/custom-mutator.ts rename to autogpt_platform/frontend/src/app/api/mutators/custom-mutator.ts index dc7dd1065c..53b899dfd5 100644 --- a/autogpt_platform/frontend/src/api/mutators/custom-mutator.ts +++ b/autogpt_platform/frontend/src/app/api/mutators/custom-mutator.ts @@ -1,7 +1,4 @@ -import { getSupabaseClient } from "@/lib/supabase/getSupabaseClient"; - -const BASE_URL = - process.env.NEXT_PUBLIC_AGPT_SERVER_BASE_URL || "http://localhost:8006"; +const BASE_URL = `${process.env.NEXT_PUBLIC_FRONTEND_BASE_URL}/api/proxy`; // Sending request via nextjs Server const getBody = (c: Response | Request): Promise => { const contentType = c.headers.get("content-type"); @@ -17,18 +14,6 @@ const getBody = (c: Response | Request): Promise => { return c.text() as Promise; }; -const getSupabaseToken = async () => { - const supabase = await getSupabaseClient(); - - const { - data: { session }, - } = (await supabase?.auth.getSession()) || { - data: { session: null }, - }; - - return session?.access_token; -}; - export const customMutator = async ( url: string, options: RequestInit & { @@ -47,12 +32,6 @@ export const customMutator = async ( ...((requestOptions.headers as Record) || {}), }; - const token = await getSupabaseToken(); - - if (token) { - headers["Authorization"] = `Bearer ${token}`; - } - const isFormData = data instanceof FormData; // Currently, only two content types are handled here: application/json and multipart/form-data @@ -75,7 +54,7 @@ export const customMutator = async ( return { status: response.status, - response_data, + data: response_data, headers: response.headers, } as T; }; diff --git a/autogpt_platform/frontend/src/api/openapi.json b/autogpt_platform/frontend/src/app/api/openapi.json similarity index 93% rename from autogpt_platform/frontend/src/api/openapi.json rename to autogpt_platform/frontend/src/app/api/openapi.json index 40a88952b1..ed31db234d 100644 --- a/autogpt_platform/frontend/src/api/openapi.json +++ b/autogpt_platform/frontend/src/app/api/openapi.json @@ -186,14 +186,16 @@ "oneOf": [ { "$ref": "#/components/schemas/OAuth2Credentials" }, { "$ref": "#/components/schemas/APIKeyCredentials" }, - { "$ref": "#/components/schemas/UserPasswordCredentials" } + { "$ref": "#/components/schemas/UserPasswordCredentials" }, + { "$ref": "#/components/schemas/HostScopedCredentials-Input" } ], "discriminator": { "propertyName": "type", "mapping": { "oauth2": "#/components/schemas/OAuth2Credentials", "api_key": "#/components/schemas/APIKeyCredentials", - "user_password": "#/components/schemas/UserPasswordCredentials" + "user_password": "#/components/schemas/UserPasswordCredentials", + "host_scoped": "#/components/schemas/HostScopedCredentials-Input" } }, "title": "Credentials" @@ -210,14 +212,18 @@ "oneOf": [ { "$ref": "#/components/schemas/OAuth2Credentials" }, { "$ref": "#/components/schemas/APIKeyCredentials" }, - { "$ref": "#/components/schemas/UserPasswordCredentials" } + { "$ref": "#/components/schemas/UserPasswordCredentials" }, + { + "$ref": "#/components/schemas/HostScopedCredentials-Output" + } ], "discriminator": { "propertyName": "type", "mapping": { "oauth2": "#/components/schemas/OAuth2Credentials", "api_key": "#/components/schemas/APIKeyCredentials", - "user_password": "#/components/schemas/UserPasswordCredentials" + "user_password": "#/components/schemas/UserPasswordCredentials", + "host_scoped": "#/components/schemas/HostScopedCredentials-Output" } }, "title": "Response Postv1Createcredentials" @@ -270,14 +276,18 @@ "oneOf": [ { "$ref": "#/components/schemas/OAuth2Credentials" }, { "$ref": "#/components/schemas/APIKeyCredentials" }, - { "$ref": "#/components/schemas/UserPasswordCredentials" } + { "$ref": "#/components/schemas/UserPasswordCredentials" }, + { + "$ref": "#/components/schemas/HostScopedCredentials-Output" + } ], "discriminator": { "propertyName": "type", "mapping": { "oauth2": "#/components/schemas/OAuth2Credentials", "api_key": "#/components/schemas/APIKeyCredentials", - "user_password": "#/components/schemas/UserPasswordCredentials" + "user_password": "#/components/schemas/UserPasswordCredentials", + "host_scoped": "#/components/schemas/HostScopedCredentials-Output" } }, "title": "Response Getv1Getcredential" @@ -434,9 +444,7 @@ "requestBody": { "content": { "application/json": { - "schema": { - "$ref": "#/components/schemas/Body_postV1LogRawMetric" - } + "schema": { "$ref": "#/components/schemas/LogRawMetricRequest" } } }, "required": true @@ -1533,11 +1541,24 @@ } } }, - "/api/schedules": { + "/api/graphs/{graph_id}/schedules": { "post": { "tags": ["v1", "schedules"], "summary": "Create execution schedule", "operationId": "postV1Create execution schedule", + "parameters": [ + { + "name": "graph_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "ID of the graph to schedule", + "title": "Graph Id" + }, + "description": "ID of the graph to schedule" + } + ], "requestBody": { "required": true, "content": { @@ -1571,17 +1592,14 @@ }, "get": { "tags": ["v1", "schedules"], - "summary": "List execution schedules", - "operationId": "getV1List execution schedules", + "summary": "List execution schedules for a graph", + "operationId": "getV1List execution schedules for a graph", "parameters": [ { "name": "graph_id", - "in": "query", - "required": false, - "schema": { - "anyOf": [{ "type": "string" }, { "type": "null" }], - "title": "Graph Id" - } + "in": "path", + "required": true, + "schema": { "type": "string", "title": "Graph Id" } } ], "responses": { @@ -1594,7 +1612,7 @@ "items": { "$ref": "#/components/schemas/GraphExecutionJobInfo" }, - "title": "Response Getv1List Execution Schedules" + "title": "Response Getv1List Execution Schedules For A Graph" } } } @@ -1610,6 +1628,29 @@ } } }, + "/api/schedules": { + "get": { + "tags": ["v1", "schedules"], + "summary": "List execution schedules for a user", + "operationId": "getV1List execution schedules for a user", + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { + "items": { + "$ref": "#/components/schemas/GraphExecutionJobInfo" + }, + "type": "array", + "title": "Response Getv1List Execution Schedules For A User" + } + } + } + } + } + } + }, "/api/schedules/{schedule_id}": { "delete": { "tags": ["v1", "schedules"], @@ -1620,7 +1661,12 @@ "name": "schedule_id", "in": "path", "required": true, - "schema": { "type": "string", "title": "Schedule Id" } + "schema": { + "type": "string", + "description": "ID of the schedule to delete", + "title": "Schedule Id" + }, + "description": "ID of the schedule to delete" } ], "responses": { @@ -2931,18 +2977,6 @@ "in": "path", "required": true, "schema": { "type": "string", "title": "Preset Id" } - }, - { - "name": "graph_id", - "in": "query", - "required": true, - "schema": { "type": "string", "title": "Graph Id" } - }, - { - "name": "graph_version", - "in": "query", - "required": true, - "schema": { "type": "integer", "title": "Graph Version" } } ], "requestBody": { @@ -3128,11 +3162,11 @@ } } }, - "put": { + "patch": { "tags": ["v2", "library", "private"], "summary": "Update Library Agent", - "description": "Update the library agent with the given fields.\n\nArgs:\n library_agent_id: ID of the library agent to update.\n payload: Fields to update (auto_update_version, is_favorite, etc.).\n user_id: ID of the authenticated user.\n\nReturns:\n 204 (No Content) on success.\n\nRaises:\n HTTPException(500): If a server/database error occurs.", - "operationId": "putV2Update library agent", + "description": "Update the library agent with the given fields.\n\nArgs:\n library_agent_id: ID of the library agent to update.\n payload: Fields to update (auto_update_version, is_favorite, etc.).\n user_id: ID of the authenticated user.\n\nRaises:\n HTTPException(500): If a server/database error occurs.", + "operationId": "patchV2Update library agent", "parameters": [ { "name": "library_agent_id", @@ -3152,7 +3186,14 @@ } }, "responses": { - "204": { "description": "Agent updated successfully" }, + "200": { + "description": "Agent updated successfully", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/LibraryAgent" } + } + } + }, "500": { "description": "Server error" }, "422": { "description": "Validation Error", @@ -3163,6 +3204,79 @@ } } } + }, + "delete": { + "tags": ["v2", "library", "private"], + "summary": "Delete Library Agent", + "description": "Soft-delete the specified library agent.\n\nArgs:\n library_agent_id: ID of the library agent to delete.\n user_id: ID of the authenticated user.\n\nReturns:\n 204 No Content if successful.\n\nRaises:\n HTTPException(404): If the agent does not exist.\n HTTPException(500): If a server/database error occurs.", + "operationId": "deleteV2Delete library agent", + "parameters": [ + { + "name": "library_agent_id", + "in": "path", + "required": true, + "schema": { "type": "string", "title": "Library Agent Id" } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { "application/json": { "schema": {} } } + }, + "204": { "description": "Agent deleted successfully" }, + "404": { "description": "Agent not found" }, + "500": { "description": "Server error" }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/HTTPValidationError" } + } + } + } + } + } + }, + "/api/library/agents/by-graph/{graph_id}": { + "get": { + "tags": ["v2", "library", "private"], + "summary": "Get Library Agent By Graph Id", + "operationId": "getV2GetLibraryAgentByGraphId", + "parameters": [ + { + "name": "graph_id", + "in": "path", + "required": true, + "schema": { "type": "string", "title": "Graph Id" } + }, + { + "name": "version", + "in": "query", + "required": false, + "schema": { + "anyOf": [{ "type": "integer" }, { "type": "null" }], + "title": "Version" + } + } + ], + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/LibraryAgent" } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/HTTPValidationError" } + } + } + } + } } }, "/api/library/agents/marketplace/{store_listing_version_id}": { @@ -3238,6 +3352,55 @@ } } }, + "/api/library/agents/{library_agent_id}/setup-trigger": { + "post": { + "tags": ["v2", "library", "private"], + "summary": "Setup Trigger", + "description": "Sets up a webhook-triggered `LibraryAgentPreset` for a `LibraryAgent`.\nReturns the correspondingly created `LibraryAgentPreset` with `webhook_id` set.", + "operationId": "postV2SetupTrigger", + "parameters": [ + { + "name": "library_agent_id", + "in": "path", + "required": true, + "schema": { + "type": "string", + "description": "ID of the library agent", + "title": "Library Agent Id" + }, + "description": "ID of the library agent" + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/TriggeredPresetSetupParams" + } + } + } + }, + "responses": { + "200": { + "description": "Successful Response", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/LibraryAgentPreset" } + } + } + }, + "422": { + "description": "Validation Error", + "content": { + "application/json": { + "schema": { "$ref": "#/components/schemas/HTTPValidationError" } + } + } + } + } + } + }, "/api/otto/ask": { "post": { "tags": ["v2", "otto"], @@ -3680,16 +3843,6 @@ "required": ["type", "data", "data_index"], "title": "Body_postV1LogRawAnalytics" }, - "Body_postV1LogRawMetric": { - "properties": { - "metric_name": { "type": "string", "title": "Metric Name" }, - "metric_value": { "type": "number", "title": "Metric Value" }, - "data_string": { "type": "string", "title": "Data String" } - }, - "type": "object", - "required": ["metric_name", "metric_value", "data_string"], - "title": "Body_postV1LogRawMetric" - }, "Body_postV2Add_credits_to_user": { "properties": { "user_id": { "type": "string", "title": "User Id" }, @@ -3713,10 +3866,10 @@ }, "Body_postV2Execute_a_preset": { "properties": { - "node_input": { + "inputs": { "additionalProperties": true, "type": "object", - "title": "Node Input" + "title": "Inputs" } }, "type": "object", @@ -3902,7 +4055,7 @@ "provider": { "$ref": "#/components/schemas/ProviderName" }, "type": { "type": "string", - "enum": ["api_key", "oauth2", "user_password"], + "enum": ["api_key", "oauth2", "user_password", "host_scoped"], "title": "Type" } }, @@ -3918,7 +4071,7 @@ "provider": { "type": "string", "title": "Provider" }, "type": { "type": "string", - "enum": ["api_key", "oauth2", "user_password"], + "enum": ["api_key", "oauth2", "user_password", "host_scoped"], "title": "Type" }, "title": { @@ -3935,6 +4088,11 @@ "username": { "anyOf": [{ "type": "string" }, { "type": "null" }], "title": "Username" + }, + "host": { + "anyOf": [{ "type": "string" }, { "type": "null" }], + "title": "Host", + "description": "Host pattern for host-scoped credentials" } }, "type": "object", @@ -4067,26 +4225,33 @@ }, "GraphExecutionJobInfo": { "properties": { + "user_id": { "type": "string", "title": "User Id" }, "graph_id": { "type": "string", "title": "Graph Id" }, + "graph_version": { "type": "integer", "title": "Graph Version" }, + "cron": { "type": "string", "title": "Cron" }, "input_data": { "additionalProperties": true, "type": "object", "title": "Input Data" }, - "user_id": { "type": "string", "title": "User Id" }, - "graph_version": { "type": "integer", "title": "Graph Version" }, - "cron": { "type": "string", "title": "Cron" }, + "input_credentials": { + "additionalProperties": { + "$ref": "#/components/schemas/CredentialsMetaInput" + }, + "type": "object", + "title": "Input Credentials" + }, "id": { "type": "string", "title": "Id" }, "name": { "type": "string", "title": "Name" }, "next_run_time": { "type": "string", "title": "Next Run Time" } }, "type": "object", "required": [ - "graph_id", - "input_data", "user_id", + "graph_id", "graph_version", "cron", + "input_data", "id", "name", "next_run_time" @@ -4276,6 +4441,70 @@ "type": "object", "title": "HTTPValidationError" }, + "HostScopedCredentials-Input": { + "properties": { + "id": { "type": "string", "title": "Id" }, + "provider": { "type": "string", "title": "Provider" }, + "title": { + "anyOf": [{ "type": "string" }, { "type": "null" }], + "title": "Title" + }, + "type": { + "type": "string", + "const": "host_scoped", + "title": "Type", + "default": "host_scoped" + }, + "host": { + "type": "string", + "title": "Host", + "description": "The host/URI pattern to match against request URLs" + }, + "headers": { + "additionalProperties": { + "type": "string", + "format": "password", + "writeOnly": true + }, + "type": "object", + "title": "Headers", + "description": "Key-value header map to add to matching requests" + } + }, + "type": "object", + "required": ["provider", "host"], + "title": "HostScopedCredentials" + }, + "HostScopedCredentials-Output": { + "properties": { + "id": { "type": "string", "title": "Id" }, + "provider": { "type": "string", "title": "Provider" }, + "title": { + "anyOf": [{ "type": "string" }, { "type": "null" }], + "title": "Title" + }, + "type": { + "type": "string", + "const": "host_scoped", + "title": "Type", + "default": "host_scoped" + }, + "host": { + "type": "string", + "title": "Host", + "description": "The host/URI pattern to match against request URLs" + }, + "headers": { + "additionalProperties": { "type": "string" }, + "type": "object", + "title": "Headers", + "description": "Key-value header map to add to matching requests" + } + }, + "type": "object", + "required": ["provider", "host"], + "title": "HostScopedCredentials" + }, "LibraryAgent": { "properties": { "id": { "type": "string", "title": "Id" }, @@ -4303,6 +4532,23 @@ "type": "object", "title": "Input Schema" }, + "credentials_input_schema": { + "additionalProperties": true, + "type": "object", + "title": "Credentials Input Schema", + "description": "Input schema for credentials required by the agent" + }, + "has_external_trigger": { + "type": "boolean", + "title": "Has External Trigger", + "description": "Whether the agent has an external trigger (e.g. webhook) node" + }, + "trigger_setup_info": { + "anyOf": [ + { "$ref": "#/components/schemas/LibraryAgentTriggerInfo" }, + { "type": "null" } + ] + }, "new_output": { "type": "boolean", "title": "New Output" }, "can_access_graph": { "type": "boolean", @@ -4326,6 +4572,8 @@ "name", "description", "input_schema", + "credentials_input_schema", + "has_external_trigger", "new_output", "can_access_graph", "is_latest_version" @@ -4342,6 +4590,13 @@ "type": "object", "title": "Inputs" }, + "credentials": { + "additionalProperties": { + "$ref": "#/components/schemas/CredentialsMetaInput" + }, + "type": "object", + "title": "Credentials" + }, "name": { "type": "string", "title": "Name" }, "description": { "type": "string", "title": "Description" }, "is_active": { @@ -4349,7 +4604,12 @@ "title": "Is Active", "default": true }, + "webhook_id": { + "anyOf": [{ "type": "string" }, { "type": "null" }], + "title": "Webhook Id" + }, "id": { "type": "string", "title": "Id" }, + "user_id": { "type": "string", "title": "User Id" }, "updated_at": { "type": "string", "format": "date-time", @@ -4361,9 +4621,11 @@ "graph_id", "graph_version", "inputs", + "credentials", "name", "description", "id", + "user_id", "updated_at" ], "title": "LibraryAgentPreset", @@ -4378,12 +4640,23 @@ "type": "object", "title": "Inputs" }, + "credentials": { + "additionalProperties": { + "$ref": "#/components/schemas/CredentialsMetaInput" + }, + "type": "object", + "title": "Credentials" + }, "name": { "type": "string", "title": "Name" }, "description": { "type": "string", "title": "Description" }, "is_active": { "type": "boolean", "title": "Is Active", "default": true + }, + "webhook_id": { + "anyOf": [{ "type": "string" }, { "type": "null" }], + "title": "Webhook Id" } }, "type": "object", @@ -4391,6 +4664,7 @@ "graph_id", "graph_version", "inputs", + "credentials", "name", "description" ], @@ -4439,6 +4713,18 @@ ], "title": "Inputs" }, + "credentials": { + "anyOf": [ + { + "additionalProperties": { + "$ref": "#/components/schemas/CredentialsMetaInput" + }, + "type": "object" + }, + { "type": "null" } + ], + "title": "Credentials" + }, "name": { "anyOf": [{ "type": "string" }, { "type": "null" }], "title": "Name" @@ -4481,6 +4767,24 @@ "enum": ["COMPLETED", "HEALTHY", "WAITING", "ERROR"], "title": "LibraryAgentStatus" }, + "LibraryAgentTriggerInfo": { + "properties": { + "provider": { "$ref": "#/components/schemas/ProviderName" }, + "config_schema": { + "additionalProperties": true, + "type": "object", + "title": "Config Schema", + "description": "Input schema for the trigger block" + }, + "credentials_input_name": { + "anyOf": [{ "type": "string" }, { "type": "null" }], + "title": "Credentials Input Name" + } + }, + "type": "object", + "required": ["provider", "config_schema", "credentials_input_name"], + "title": "LibraryAgentTriggerInfo" + }, "LibraryAgentUpdateRequest": { "properties": { "auto_update_version": { @@ -4497,11 +4801,6 @@ "anyOf": [{ "type": "boolean" }, { "type": "null" }], "title": "Is Archived", "description": "Archive the agent" - }, - "is_deleted": { - "anyOf": [{ "type": "boolean" }, { "type": "null" }], - "title": "Is Deleted", - "description": "Delete the agent" } }, "type": "object", @@ -4525,6 +4824,24 @@ "required": ["source_id", "sink_id", "source_name", "sink_name"], "title": "Link" }, + "LogRawMetricRequest": { + "properties": { + "metric_name": { + "type": "string", + "minLength": 1, + "title": "Metric Name" + }, + "metric_value": { "type": "number", "title": "Metric Value" }, + "data_string": { + "type": "string", + "minLength": 1, + "title": "Data String" + } + }, + "type": "object", + "required": ["metric_name", "metric_value", "data_string"], + "title": "LogRawMetricRequest" + }, "LoginResponse": { "properties": { "login_url": { "type": "string", "title": "Login Url" }, @@ -4867,6 +5184,7 @@ "AGENT_INPUT", "CONGRATS", "GET_RESULTS", + "RUN_AGENTS", "MARKETPLACE_VISIT", "MARKETPLACE_ADD_AGENT", "MARKETPLACE_RUN_AGENT", @@ -5271,6 +5589,7 @@ "google", "google_maps", "groq", + "http", "hubspot", "ideogram", "jina", @@ -5362,17 +5681,27 @@ }, "ScheduleCreationRequest": { "properties": { + "graph_version": { + "anyOf": [{ "type": "integer" }, { "type": "null" }], + "title": "Graph Version" + }, + "name": { "type": "string", "title": "Name" }, "cron": { "type": "string", "title": "Cron" }, - "input_data": { + "inputs": { "additionalProperties": true, "type": "object", - "title": "Input Data" + "title": "Inputs" }, - "graph_id": { "type": "string", "title": "Graph Id" }, - "graph_version": { "type": "integer", "title": "Graph Version" } + "credentials": { + "additionalProperties": { + "$ref": "#/components/schemas/CredentialsMetaInput" + }, + "type": "object", + "title": "Credentials" + } }, "type": "object", - "required": ["cron", "input_data", "graph_id", "graph_version"], + "required": ["name", "cron", "inputs"], "title": "ScheduleCreationRequest" }, "SetGraphActiveVersion": { @@ -5773,6 +6102,31 @@ "required": ["transactions", "next_transaction_time"], "title": "TransactionHistory" }, + "TriggeredPresetSetupParams": { + "properties": { + "name": { "type": "string", "title": "Name" }, + "description": { + "type": "string", + "title": "Description", + "default": "" + }, + "trigger_config": { + "additionalProperties": true, + "type": "object", + "title": "Trigger Config" + }, + "agent_credentials": { + "additionalProperties": { + "$ref": "#/components/schemas/CredentialsMetaInput" + }, + "type": "object", + "title": "Agent Credentials" + } + }, + "type": "object", + "required": ["name", "trigger_config"], + "title": "TriggeredPresetSetupParams" + }, "TurnstileVerifyRequest": { "properties": { "token": { @@ -6055,16 +6409,6 @@ "type": "string", "title": "Provider Webhook Id" }, - "attached_nodes": { - "anyOf": [ - { - "items": { "$ref": "#/components/schemas/NodeModel" }, - "type": "array" - }, - { "type": "null" } - ], - "title": "Attached Nodes" - }, "url": { "type": "string", "title": "Url", "readOnly": true } }, "type": "object", diff --git a/autogpt_platform/frontend/src/app/api/proxy/[...path]/route.ts b/autogpt_platform/frontend/src/app/api/proxy/[...path]/route.ts new file mode 100644 index 0000000000..4ef22629ba --- /dev/null +++ b/autogpt_platform/frontend/src/app/api/proxy/[...path]/route.ts @@ -0,0 +1,158 @@ +import { + makeAuthenticatedFileUpload, + makeAuthenticatedRequest, +} from "@/lib/autogpt-server-api/helpers"; +import { NextRequest, NextResponse } from "next/server"; + +function getBackendBaseUrl() { + if (process.env.NEXT_PUBLIC_AGPT_SERVER_URL) { + return process.env.NEXT_PUBLIC_AGPT_SERVER_URL.replace("/api", ""); + } + + return "http://localhost:8006"; +} + +function buildBackendUrl(path: string[], queryString: string): string { + const backendPath = path.join("/"); + return `${getBackendBaseUrl()}/${backendPath}${queryString}`; +} + +async function handleJsonRequest( + req: NextRequest, + method: string, + backendUrl: string, +): Promise { + const payload = await req.json(); + return await makeAuthenticatedRequest( + method, + backendUrl, + payload, + "application/json", + ); +} + +async function handleFormDataRequest( + req: NextRequest, + backendUrl: string, +): Promise { + const formData = await req.formData(); + return await makeAuthenticatedFileUpload(backendUrl, formData); +} + +async function handleUrlEncodedRequest( + req: NextRequest, + method: string, + backendUrl: string, +): Promise { + const textPayload = await req.text(); + const params = new URLSearchParams(textPayload); + const payload = Object.fromEntries(params.entries()); + return await makeAuthenticatedRequest( + method, + backendUrl, + payload, + "application/x-www-form-urlencoded", + ); +} + +async function handleRequestWithoutBody( + method: string, + backendUrl: string, +): Promise { + return await makeAuthenticatedRequest(method, backendUrl); +} + +function createUnsupportedContentTypeResponse( + contentType: string | null, +): NextResponse { + return NextResponse.json( + { + error: + "Unsupported Content-Type for proxying with authentication helpers.", + receivedContentType: contentType, + supportedContentTypes: [ + "application/json", + "multipart/form-data", + "application/x-www-form-urlencoded", + ], + }, + { status: 415 }, // Unsupported Media Type + ); +} + +function createResponse( + responseBody: any, + responseStatus: number, + responseHeaders: Record, +): NextResponse { + if (responseStatus === 204) { + return new NextResponse(null, { status: responseStatus }); + } else { + return NextResponse.json(responseBody, { + status: responseStatus, + headers: responseHeaders, + }); + } +} + +function createErrorResponse(error: unknown): NextResponse { + console.error("API proxy error:", error); + const detail = + error instanceof Error ? error.message : "An unknown error occurred"; + return NextResponse.json( + { error: "Proxy request failed", detail }, + { status: 500 }, // Internal Server Error + ); +} + +/** + * A simple proxy route that forwards requests to the backend API. + * It injects the server-side authentication token into the Authorization header. + * It uses the makeAuthenticatedRequest and makeAuthenticatedFileUpload helpers + * to handle request body parsing and authentication. + */ +async function handler( + req: NextRequest, + { params }: { params: Promise<{ path: string[] }> }, +) { + const { path } = await params; + const url = new URL(req.url); + const queryString = url.search; + const backendUrl = buildBackendUrl(path, queryString); + + const method = req.method; + const contentType = req.headers.get("Content-Type"); + + let responseBody: any; + const responseStatus: number = 200; + const responseHeaders: Record = { + "Content-Type": "application/json", + }; + + try { + if (method === "GET" || method === "DELETE") { + responseBody = await handleRequestWithoutBody(method, backendUrl); + } else if (contentType?.includes("application/json")) { + responseBody = await handleJsonRequest(req, method, backendUrl); + } else if (contentType?.includes("multipart/form-data")) { + responseBody = await handleFormDataRequest(req, backendUrl); + responseHeaders["Content-Type"] = "text/plain"; + } else if (contentType?.includes("application/x-www-form-urlencoded")) { + responseBody = await handleUrlEncodedRequest(req, method, backendUrl); + } else { + return createUnsupportedContentTypeResponse(contentType); + } + + return createResponse(responseBody, responseStatus, responseHeaders); + } catch (error) { + return createErrorResponse(error); + } +} + +export { + handler as DELETE, + handler as GET, + handler as PATCH, + handler as POST, + handler as PUT, +}; diff --git a/autogpt_platform/frontend/src/api/transformers/fix-tags.mjs b/autogpt_platform/frontend/src/app/api/transformers/fix-tags.mjs similarity index 100% rename from autogpt_platform/frontend/src/api/transformers/fix-tags.mjs rename to autogpt_platform/frontend/src/app/api/transformers/fix-tags.mjs diff --git a/autogpt_platform/frontend/src/components/CustomNode.tsx b/autogpt_platform/frontend/src/components/CustomNode.tsx index aa87ba30e2..21a10b87a9 100644 --- a/autogpt_platform/frontend/src/components/CustomNode.tsx +++ b/autogpt_platform/frontend/src/components/CustomNode.tsx @@ -268,7 +268,7 @@ export const CustomNode = React.memo( default: const getInputPropKey = (key: string) => - nodeType == BlockUIType.AGENT ? `data.${key}` : key; + nodeType == BlockUIType.AGENT ? `inputs.${key}` : key; return keys.map(([propKey, propSchema]) => { const isRequired = data.inputSchema.required?.includes(propKey); diff --git a/autogpt_platform/frontend/src/components/Flow.tsx b/autogpt_platform/frontend/src/components/Flow.tsx index 345fc60b5a..5dbcd65362 100644 --- a/autogpt_platform/frontend/src/components/Flow.tsx +++ b/autogpt_platform/frontend/src/components/Flow.tsx @@ -4,10 +4,12 @@ import React, { useState, useCallback, useEffect, + useMemo, useRef, MouseEvent, Suspense, } from "react"; +import Link from "next/link"; import { ReactFlow, ReactFlowProvider, @@ -32,7 +34,9 @@ import { formatEdgeID, GraphExecutionID, GraphID, + LibraryAgent, } from "@/lib/autogpt-server-api"; +import { useBackendAPI } from "@/lib/autogpt-server-api/context"; import { getTypeColor, findNewlyAddedBlockCoordinates } from "@/lib/utils"; import { history } from "./history"; import { CustomEdge } from "./CustomEdge"; @@ -41,6 +45,7 @@ import { Control, ControlPanel } from "@/components/edit/control/ControlPanel"; import { SaveControl } from "@/components/edit/control/SaveControl"; import { BlocksControl } from "@/components/edit/control/BlocksControl"; import { IconUndo2, IconRedo2 } from "@/components/ui/icons"; +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { startTutorial } from "./tutorial"; import useAgentGraph from "@/hooks/useAgentGraph"; import { v4 as uuidv4 } from "uuid"; @@ -48,11 +53,11 @@ import { useRouter, usePathname, useSearchParams } from "next/navigation"; import RunnerUIWrapper, { RunnerUIWrapperRef, } from "@/components/RunnerUIWrapper"; +import { CronSchedulerDialog } from "@/components/cron-scheduler-dialog"; import PrimaryActionBar from "@/components/PrimaryActionButton"; import OttoChatWidget from "@/components/OttoChatWidget"; import { useToast } from "@/components/ui/use-toast"; import { useCopyPaste } from "../hooks/useCopyPaste"; -import { CronScheduler } from "./cronScheduler"; // This is for the history, this is the minimum distance a block must move before it is logged // It helps to prevent spamming the history with small movements especially when pressing on a input in a block @@ -77,7 +82,7 @@ export const FlowContext = createContext(null); const FlowEditor: React.FC<{ flowID?: GraphID; - flowVersion?: string; + flowVersion?: number; className?: string; }> = ({ flowID, flowVersion, className }) => { const { @@ -118,10 +123,24 @@ const FlowEditor: React.FC<{ setEdges, } = useAgentGraph( flowID, - flowVersion ? parseInt(flowVersion) : undefined, + flowVersion, flowExecutionID, visualizeBeads !== "no", ); + const api = useBackendAPI(); + const [libraryAgent, setLibraryAgent] = useState(null); + useEffect(() => { + if (!flowID) return; + api + .getLibraryAgentByGraphID(flowID, flowVersion) + .then((libraryAgent) => setLibraryAgent(libraryAgent)) + .catch((error) => { + console.warn( + `Failed to fetch LibraryAgent for graph #${flowID} v${flowVersion}`, + error, + ); + }); + }, [api, flowID, flowVersion]); const router = useRouter(); const pathname = usePathname(); @@ -154,6 +173,16 @@ const FlowEditor: React.FC<{ : `Builder - AutoGPT Platform`; }, [savedAgent]); + const graphHasWebhookNodes = useMemo( + () => + nodes.some((n) => + [BlockUIType.WEBHOOK, BlockUIType.WEBHOOK_MANUAL].includes( + n.data.uiType, + ), + ), + [nodes], + ); + useEffect(() => { if (params.get("resetTutorial") === "true") { localStorage.removeItem(TUTORIAL_STORAGE_KEY); @@ -639,8 +668,11 @@ const FlowEditor: React.FC<{ // This function is called after cron expression is created // So you can collect inputs for scheduling - const afterCronCreation = (cronExpression: string) => { - runnerUIRef.current?.collectInputsForScheduling(cronExpression); + const afterCronCreation = (cronExpression: string, scheduleName: string) => { + runnerUIRef.current?.collectInputsForScheduling( + cronExpression, + scheduleName, + ); }; // This function Opens up form for creating cron expression @@ -704,35 +736,62 @@ const FlowEditor: React.FC<{ /> } > - runnerUIRef.current?.openRunnerOutput()} - onClickRunAgent={() => { - if (!savedAgent) { - toast({ - title: `Please save the agent using the button in the left sidebar before running it.`, - duration: 2000, - }); - return; - } - if (!isRunning) { - runnerUIRef.current?.runOrOpenInput(); - } else { - requestStopRun(); - } - }} - onClickScheduleButton={handleScheduleButton} - isScheduling={isScheduling} - isDisabled={!savedAgent} - isRunning={isRunning} - requestStopRun={requestStopRun} - runAgentTooltip={!isRunning ? "Run Agent" : "Stop Agent"} - /> - + {!graphHasWebhookNodes ? ( + <> + + runnerUIRef.current?.openRunnerOutput() + } + onClickRunAgent={() => { + if (isRunning) return; + if (!savedAgent) { + toast({ + title: `Please save the agent using the button in the left sidebar before running it.`, + duration: 2000, + }); + return; + } + runnerUIRef.current?.runOrOpenInput(); + }} + onClickStopRun={requestStopRun} + onClickScheduleButton={handleScheduleButton} + isScheduling={isScheduling} + isDisabled={!savedAgent} + isRunning={isRunning} + /> + + + ) : ( + + You are building a Trigger Agent + + Your agent{" "} + {savedAgent?.nodes.some((node) => node.webhook) + ? "is listening" + : "will listen"}{" "} + for its trigger and will run when the time is right. +
+ You can view its activity in your + + Agent Library + + . +
+
+ )}
void; - onClickRunAgent: () => void; - onClickScheduleButton: () => void; + onClickRunAgent?: () => void; + onClickStopRun: () => void; + onClickScheduleButton?: () => void; isRunning: boolean; isDisabled: boolean; isScheduling: boolean; - requestStopRun: () => void; - runAgentTooltip: string; className?: string; } const PrimaryActionBar: React.FC = ({ onClickAgentOutputs, onClickRunAgent, + onClickStopRun, onClickScheduleButton, isRunning, isDisabled, isScheduling, - requestStopRun, - runAgentTooltip, className, }) => { - const runButtonLabel = !isRunning ? "Run" : "Stop"; - - const runButtonIcon = !isRunning ? : ; - - const runButtonOnClick = !isRunning ? onClickRunAgent : requestStopRun; - + const buttonClasses = + "flex items-center gap-2 text-sm font-medium md:text-lg"; return (
= ({ )} >
- - - - - -

View agent outputs

-
-
- - - - - -

{runAgentTooltip}

-
-
- - - - - -

Schedule this Agent

-
-
+ + + {!isRunning ? ( + + ) : ( + + )} + + {onClickScheduleButton && ( + + )}
); diff --git a/autogpt_platform/frontend/src/components/RunnerUIWrapper.tsx b/autogpt_platform/frontend/src/components/RunnerUIWrapper.tsx index 2f72754205..37ead84148 100644 --- a/autogpt_platform/frontend/src/components/RunnerUIWrapper.tsx +++ b/autogpt_platform/frontend/src/components/RunnerUIWrapper.tsx @@ -36,14 +36,21 @@ interface RunnerUIWrapperProps { isRunning: boolean; isScheduling: boolean; requestSaveAndRun: () => void; - scheduleRunner: (cronExpression: string, input: InputItem[]) => Promise; + scheduleRunner: ( + cronExpression: string, + input: InputItem[], + scheduleName: string, + ) => Promise; } export interface RunnerUIWrapperRef { openRunnerInput: () => void; openRunnerOutput: () => void; runOrOpenInput: () => void; - collectInputsForScheduling: (cronExpression: string) => void; + collectInputsForScheduling: ( + cronExpression: string, + scheduleName: string, + ) => void; } const RunnerUIWrapper = forwardRef( @@ -63,6 +70,7 @@ const RunnerUIWrapper = forwardRef( const [isRunnerOutputOpen, setIsRunnerOutputOpen] = useState(false); const [scheduledInput, setScheduledInput] = useState(false); const [cronExpression, setCronExpression] = useState(""); + const [scheduleName, setScheduleName] = useState(""); const getBlockInputsAndOutputs = useCallback((): { inputs: InputItem[]; @@ -149,15 +157,19 @@ const RunnerUIWrapper = forwardRef( } }; - const collectInputsForScheduling = (cron_exp: string) => { + const collectInputsForScheduling = ( + cronExpression: string, + scheduleName: string, + ) => { const { inputs } = getBlockInputsAndOutputs(); - setCronExpression(cron_exp); + setCronExpression(cronExpression); + setScheduleName(scheduleName); if (inputs.length > 0) { setScheduledInput(true); setIsRunnerInputOpen(true); } else { - scheduleRunner(cron_exp, []); + scheduleRunner(cronExpression, [], scheduleName); } }; @@ -186,6 +198,7 @@ const RunnerUIWrapper = forwardRef( await scheduleRunner( cronExpression, getBlockInputsAndOutputs().inputs, + scheduleName, ); setIsScheduling(false); setIsRunnerInputOpen(false); diff --git a/autogpt_platform/frontend/src/components/agents/agent-run-draft-view.tsx b/autogpt_platform/frontend/src/components/agents/agent-run-draft-view.tsx index ab531bf002..23628f0a31 100644 --- a/autogpt_platform/frontend/src/components/agents/agent-run-draft-view.tsx +++ b/autogpt_platform/frontend/src/components/agents/agent-run-draft-view.tsx @@ -1,73 +1,484 @@ "use client"; -import React, { useCallback, useMemo, useState } from "react"; +import React, { useCallback, useEffect, useMemo, useState } from "react"; import { useBackendAPI } from "@/lib/autogpt-server-api/context"; -import { GraphExecutionID, GraphMeta } from "@/lib/autogpt-server-api"; +import { + CredentialsMetaInput, + GraphExecutionID, + LibraryAgent, + LibraryAgentPreset, + LibraryAgentPresetID, + LibraryAgentPresetUpdatable, + Schedule, +} from "@/lib/autogpt-server-api"; import type { ButtonAction } from "@/components/agptui/types"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { IconCross, IconPlay, IconSave } from "@/components/ui/icons"; +import { CalendarClockIcon, Trash2Icon } from "lucide-react"; +import { CronSchedulerDialog } from "@/components/cron-scheduler-dialog"; import { CredentialsInput } from "@/components/integrations/credentials-input"; import { TypeBasedInput } from "@/components/type-based-input"; import { useToastOnFail } from "@/components/ui/use-toast"; import ActionButtonGroup from "@/components/agptui/action-button-group"; +import { useOnboarding } from "@/components/onboarding/onboarding-provider"; import SchemaTooltip from "@/components/SchemaTooltip"; -import { IconPlay } from "@/components/ui/icons"; -import { useOnboarding } from "../onboarding/onboarding-provider"; +import { useToast } from "@/components/ui/use-toast"; +import { isEmpty } from "lodash"; +import { Input } from "@/components/ui/input"; export default function AgentRunDraftView({ - graph, + agent, + agentPreset, onRun, + onCreatePreset, + onUpdatePreset, + doDeletePreset, + onCreateSchedule, agentActions, }: { - graph: GraphMeta; - onRun: (runID: GraphExecutionID) => void; + agent: LibraryAgent; agentActions: ButtonAction[]; -}): React.ReactNode { + onRun: (runID: GraphExecutionID) => void; + onCreateSchedule: (schedule: Schedule) => void; +} & ( + | { + onCreatePreset: (preset: LibraryAgentPreset) => void; + agentPreset?: never; + onUpdatePreset?: never; + doDeletePreset?: never; + } + | { + onCreatePreset?: never; + agentPreset: LibraryAgentPreset; + onUpdatePreset: (preset: LibraryAgentPreset) => void; + doDeletePreset: (presetID: LibraryAgentPresetID) => void; + } +)): React.ReactNode { const api = useBackendAPI(); + const { toast } = useToast(); const toastOnFail = useToastOnFail(); - const agentInputs = graph.input_schema.properties; - const agentCredentialsInputs = graph.credentials_input_schema.properties; const [inputValues, setInputValues] = useState>({}); - const [inputCredentials, setInputCredentials] = useState>( - {}, + const [inputCredentials, setInputCredentials] = useState< + Record + >({}); + const [presetName, setPresetName] = useState(""); + const [presetDescription, setPresetDescription] = useState(""); + const [changedPresetAttributes, setChangedPresetAttributes] = useState< + Set + >(new Set()); + const { state: onboardingState, completeStep: completeOnboardingStep } = + useOnboarding(); + const [cronScheduleDialogOpen, setCronScheduleDialogOpen] = useState(false); + + // Update values if agentPreset parameter is changed + useEffect(() => { + setInputValues(agentPreset?.inputs ?? {}); + setInputCredentials(agentPreset?.credentials ?? {}); + setPresetName(agentPreset?.name ?? ""); + setPresetDescription(agentPreset?.description ?? ""); + setChangedPresetAttributes(new Set()); + }, [agentPreset]); + + const agentInputSchema = useMemo( + () => + agent.has_external_trigger + ? agent.trigger_setup_info.config_schema + : agent.input_schema, + [agent], + ); + const agentInputFields = useMemo( + () => + Object.fromEntries( + Object.entries(agentInputSchema.properties).filter( + ([_, subSchema]) => !subSchema.hidden, + ), + ), + [agentInputSchema], + ); + const agentCredentialsInputFields = useMemo( + () => agent.credentials_input_schema.properties, + [agent], + ); + + const [allRequiredInputsAreSet, missingInputs] = useMemo(() => { + const nonEmptyInputs = new Set( + Object.keys(inputValues).filter((k) => !isEmpty(inputValues[k])), + ); + const requiredInputs = new Set( + agentInputSchema.required as string[] | undefined, + ); + return [ + nonEmptyInputs.isSupersetOf(requiredInputs), + [...requiredInputs.difference(nonEmptyInputs)], + ]; + }, [agentInputSchema.required, inputValues]); + const [allCredentialsAreSet, missingCredentials] = useMemo(() => { + const availableCredentials = new Set(Object.keys(inputCredentials)); + const allCredentials = new Set(Object.keys(agentCredentialsInputFields)); + return [ + availableCredentials.isSupersetOf(allCredentials), + [...allCredentials.difference(availableCredentials)], + ]; + }, [agentCredentialsInputFields, inputCredentials]); + const notifyMissingInputs = useCallback( + (needPresetName: boolean = true) => { + const allMissingFields = ( + needPresetName && !presetName + ? [agent.has_external_trigger ? "trigger_name" : "preset_name"] + : [] + ) + .concat(missingInputs) + .concat(missingCredentials); + toast({ + title: "⚠️ Not all required inputs are set", + description: `Please set ${allMissingFields.map((k) => `\`${k}\``).join(", ")}`, + }); + }, + [missingInputs, missingCredentials], ); - const { state, completeStep } = useOnboarding(); const doRun = useCallback(() => { - api - .executeGraph(graph.id, graph.version, inputValues, inputCredentials) - .then((newRun) => onRun(newRun.graph_exec_id)) - .catch(toastOnFail("execute agent")); + // Manually running webhook-triggered agents is not supported + if (agent.has_external_trigger) return; + + if (!agentPreset || changedPresetAttributes.size > 0) { + if (!allRequiredInputsAreSet || !allCredentialsAreSet) { + notifyMissingInputs(false); + return; + } + // TODO: on executing preset with changes, ask for confirmation and offer save+run + api + .executeGraph( + agent.graph_id, + agent.graph_version, + inputValues, + inputCredentials, + ) + .then((newRun) => onRun(newRun.graph_exec_id)) + .catch(toastOnFail("execute agent")); + } else { + api + .executeLibraryAgentPreset(agentPreset.id) + .then((newRun) => onRun(newRun.id)) + .catch(toastOnFail("execute agent preset")); + } // Mark run agent onboarding step as completed - if (state?.completedSteps.includes("MARKETPLACE_ADD_AGENT")) { - completeStep("MARKETPLACE_RUN_AGENT"); + if (onboardingState?.completedSteps.includes("MARKETPLACE_ADD_AGENT")) { + completeOnboardingStep("MARKETPLACE_RUN_AGENT"); } }, [ api, - graph, + agent, inputValues, inputCredentials, onRun, toastOnFail, - state, - completeStep, + onboardingState, + completeOnboardingStep, ]); + const doCreatePreset = useCallback(() => { + if (!onCreatePreset) return; + + if (!presetName || !allRequiredInputsAreSet || !allCredentialsAreSet) { + notifyMissingInputs(); + return; + } + + api + .createLibraryAgentPreset({ + name: presetName, + description: presetDescription, + graph_id: agent.graph_id, + graph_version: agent.graph_version, + inputs: inputValues, + credentials: inputCredentials, + }) + .then((newPreset) => { + onCreatePreset(newPreset); + setChangedPresetAttributes(new Set()); // reset change tracker + }) + .catch(toastOnFail("save agent preset")); + }, [ + api, + agent, + presetName, + presetDescription, + inputValues, + inputCredentials, + onCreatePreset, + toast, + toastOnFail, + onboardingState, + completeOnboardingStep, + ]); + + const doUpdatePreset = useCallback(() => { + if (!agentPreset || changedPresetAttributes.size == 0) return; + + if (!presetName || !allRequiredInputsAreSet || !allCredentialsAreSet) { + notifyMissingInputs(); + return; + } + + const updatePreset: LibraryAgentPresetUpdatable = {}; + if (changedPresetAttributes.has("name")) updatePreset["name"] = presetName; + if (changedPresetAttributes.has("description")) + updatePreset["description"] = presetDescription; + if ( + changedPresetAttributes.has("inputs") || + changedPresetAttributes.has("credentials") + ) { + updatePreset["inputs"] = inputValues; + updatePreset["credentials"] = inputCredentials; + } + api + .updateLibraryAgentPreset(agentPreset.id, updatePreset) + .then((updatedPreset) => { + onUpdatePreset(updatedPreset); + setChangedPresetAttributes(new Set()); // reset change tracker + }) + .catch(toastOnFail("update agent preset")); + }, [ + api, + agent, + presetName, + presetDescription, + inputValues, + inputCredentials, + onUpdatePreset, + toast, + toastOnFail, + onboardingState, + completeOnboardingStep, + ]); + + const doSetPresetActive = useCallback( + async (active: boolean) => { + if (!agentPreset) return; + const updatedPreset = await api.updateLibraryAgentPreset(agentPreset.id, { + is_active: active, + }); + onUpdatePreset(updatedPreset); + }, + [agentPreset, api, onUpdatePreset], + ); + + const doSetupTrigger = useCallback(() => { + // Setting up a trigger for non-webhook-triggered agents is not supported + if (!agent.has_external_trigger || !onCreatePreset) return; + + if (!presetName || !allRequiredInputsAreSet || !allCredentialsAreSet) { + notifyMissingInputs(); + return; + } + + const credentialsInputName = + agent.trigger_setup_info.credentials_input_name; + + if (!credentialsInputName) { + // FIXME: implement support for manual-setup webhooks + toast({ + variant: "destructive", + title: "🚧 Feature under construction", + description: "Setting up non-auto-setup triggers is not yet supported.", + }); + return; + } + + api + .setupAgentTrigger(agent.id, { + name: presetName, + description: presetDescription, + trigger_config: inputValues, + agent_credentials: inputCredentials, + }) + .then((newPreset) => { + onCreatePreset(newPreset); + setChangedPresetAttributes(new Set()); // reset change tracker + }) + .catch(toastOnFail("set up agent trigger")); + + // Mark run agent onboarding step as completed(?) + if (onboardingState?.completedSteps.includes("MARKETPLACE_ADD_AGENT")) { + completeOnboardingStep("MARKETPLACE_RUN_AGENT"); + } + }, [ + api, + agent, + presetName, + presetDescription, + inputValues, + inputCredentials, + onCreatePreset, + toast, + toastOnFail, + onboardingState, + completeOnboardingStep, + ]); + + const openScheduleDialog = useCallback(() => { + // Scheduling is not supported for webhook-triggered agents + if (agent.has_external_trigger) return; + + if (!allRequiredInputsAreSet || !allCredentialsAreSet) { + notifyMissingInputs(false); + return; + } + + setCronScheduleDialogOpen(true); + }, [ + agent, + allRequiredInputsAreSet, + allCredentialsAreSet, + notifyMissingInputs, + ]); + + const doSetupSchedule = useCallback( + (cronExpression: string, scheduleName: string) => { + // Scheduling is not supported for webhook-triggered agents + if (agent.has_external_trigger) return; + + api + .createGraphExecutionSchedule({ + graph_id: agent.graph_id, + graph_version: agent.graph_version, + name: scheduleName || agent.name, + cron: cronExpression, + inputs: inputValues, + credentials: inputCredentials, + }) + .then((schedule) => onCreateSchedule(schedule)) + .catch(toastOnFail("set up agent run schedule")); + }, + [api, agent, inputValues, inputCredentials, onCreateSchedule, toastOnFail], + ); + const runActions: ButtonAction[] = useMemo( () => [ - { - label: ( - <> - - Run - - ), - variant: "accent", - callback: doRun, - }, + // "Regular" agent: [run] + [save as preset] buttons + ...(!agent.has_external_trigger + ? ([ + { + label: ( + <> + Run + + ), + variant: "accent", + callback: doRun, + }, + { + label: ( + <> + Schedule + + ), + callback: openScheduleDialog, + }, + // { + // label: ( + // <> + // Save as a preset + // + // ), + // callback: doCreatePreset, + // disabled: !( + // presetName && + // allRequiredInputsAreSet && + // allCredentialsAreSet + // ), + // }, + ] satisfies ButtonAction[]) + : []), + // Triggered agent: [setup] button + ...(agent.has_external_trigger && !agentPreset?.webhook_id + ? ([ + { + label: ( + <> + Set up trigger + + ), + variant: "accent", + callback: doSetupTrigger, + disabled: !( + presetName && + allRequiredInputsAreSet && + allCredentialsAreSet + ), + }, + ] satisfies ButtonAction[]) + : []), + // Existing agent trigger: [enable]/[disable] button + ...(agentPreset?.webhook_id + ? ([ + agentPreset.is_active + ? { + label: ( + <> + Disable trigger + + ), + variant: "destructive", + callback: () => doSetPresetActive(false), + } + : { + label: ( + <> + Enable trigger + + ), + variant: "accent", + callback: () => doSetPresetActive(true), + }, + ] satisfies ButtonAction[]) + : []), + // Existing agent preset/trigger: [save] and [delete] buttons + ...(agentPreset + ? ([ + { + label: ( + <> + Save changes + + ), + callback: doUpdatePreset, + disabled: !( + changedPresetAttributes.size > 0 && + presetName && + allRequiredInputsAreSet && + allCredentialsAreSet + ), + }, + { + label: ( + <> + + Delete {agent.has_external_trigger ? "trigger" : "preset"} + + ), + callback: () => doDeletePreset(agentPreset.id), + }, + ] satisfies ButtonAction[]) + : []), + ], + [ + agent.has_external_trigger, + agentPreset, + doRun, + doSetupTrigger, + doCreatePreset, + doUpdatePreset, + doDeletePreset, + openScheduleDialog, + changedPresetAttributes, + presetName, + allRequiredInputsAreSet, + allCredentialsAreSet, ], - [doRun], ); return ( @@ -78,8 +489,49 @@ export default function AgentRunDraftView({ Input + {(agentPreset || agent.has_external_trigger) && ( + <> + {/* Preset name and description */} +
+ + { + setPresetName(e.target.value); + setChangedPresetAttributes((prev) => prev.add("name")); + }} + /> +
+
+ + { + setPresetDescription(e.target.value); + setChangedPresetAttributes((prev) => + prev.add("description"), + ); + }} + /> +
+ + )} + {/* Credentials inputs */} - {Object.entries(agentCredentialsInputs).map( + {Object.entries(agentCredentialsInputFields).map( ([key, inputSubSchema]) => ( - setInputCredentials((obj) => ({ - ...obj, - [key]: value, - })) + onSelectCredentials={(value) => { + setInputCredentials((obj) => { + const newObj = { ...obj }; + if (value === undefined) { + delete newObj[key]; + return newObj; + } + return { + ...obj, + [key]: value, + }; + }); + setChangedPresetAttributes((prev) => + prev.add("credentials"), + ); + }} + hideIfSingleCredentialAvailable={ + !agentPreset && !agent.has_external_trigger } /> ), )} {/* Regular inputs */} - {Object.entries(agentInputs).map(([key, inputSubSchema]) => ( + {Object.entries(agentInputFields).map(([key, inputSubSchema]) => (
))} @@ -125,7 +591,16 @@ export default function AgentRunDraftView({ {/* Actions */}
@@ -105,41 +122,67 @@ export default function AgentRunsSelectorList({ onClick={onSelectDraftNewRun} > - New run + New {agent.has_external_trigger ? "trigger" : "run"} )} - {activeListTab === "runs" - ? agentRuns + {activeListTab === "runs" ? ( + <> + {agentPresets + .toSorted( + (a, b) => b.updated_at.getTime() - a.updated_at.getTime(), + ) + .map((preset) => ( + onSelectPreset(preset.id)} + onDelete={() => doDeletePreset(preset.id)} + /> + ))} + {agentPresets.length > 0 && } + {agentRuns .toSorted( (a, b) => b.started_at.getTime() - a.started_at.getTime(), ) .map((run) => ( p.id == run.preset_id)?.name + : null) ?? agent.name + } timestamp={run.started_at} selected={selectedView.id === run.id} onClick={() => onSelectRun(run.id)} - onDelete={() => onDeleteRun(run)} - /> - )) - : schedules - .filter((schedule) => schedule.graph_id === agent.graph_id) - .map((schedule) => ( - onSelectSchedule(schedule)} - onDelete={() => onDeleteSchedule(schedule.id)} + onDelete={() => doDeleteRun(run)} /> ))} + + ) : ( + schedules.map((schedule) => ( + onSelectSchedule(schedule.id)} + onDelete={() => doDeleteSchedule(schedule.id)} + /> + )) + )}
diff --git a/autogpt_platform/frontend/src/components/agents/agent-schedule-details-view.tsx b/autogpt_platform/frontend/src/components/agents/agent-schedule-details-view.tsx index da03527a79..2ae2c2fde0 100644 --- a/autogpt_platform/frontend/src/components/agents/agent-schedule-details-view.tsx +++ b/autogpt_platform/frontend/src/components/agents/agent-schedule-details-view.tsx @@ -5,27 +5,33 @@ import { GraphExecutionID, GraphMeta, Schedule, + ScheduleID, } from "@/lib/autogpt-server-api"; import { useBackendAPI } from "@/lib/autogpt-server-api/context"; import type { ButtonAction } from "@/components/agptui/types"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { humanizeCronExpression } from "@/lib/cron-expression-utils"; import { AgentRunStatus } from "@/components/agents/agent-run-status-chip"; import { useToastOnFail } from "@/components/ui/use-toast"; import ActionButtonGroup from "@/components/agptui/action-button-group"; +import { IconCross } from "@/components/ui/icons"; +import { PlayIcon } from "lucide-react"; import LoadingBox from "@/components/ui/loading"; import { Input } from "@/components/ui/input"; export default function AgentScheduleDetailsView({ graph, schedule, - onForcedRun, agentActions, + onForcedRun, + doDeleteSchedule, }: { graph: GraphMeta; schedule: Schedule; - onForcedRun: (runID: GraphExecutionID) => void; agentActions: ButtonAction[]; + onForcedRun: (runID: GraphExecutionID) => void; + doDeleteSchedule: (scheduleID: ScheduleID) => void; }): React.ReactNode { const api = useBackendAPI(); @@ -42,7 +48,11 @@ export default function AgentScheduleDetailsView({ selectedRunStatus.slice(1), }, { - label: "Scheduled for", + label: "Schedule", + value: humanizeCronExpression(schedule.cron), + }, + { + label: "Next run", value: schedule.next_run_time.toLocaleString(), }, ]; @@ -70,14 +80,39 @@ export default function AgentScheduleDetailsView({ const runNow = useCallback( () => api - .executeGraph(graph.id, graph.version, schedule.input_data) + .executeGraph( + graph.id, + graph.version, + schedule.input_data, + schedule.input_credentials, + ) .then((run) => onForcedRun(run.graph_exec_id)) .catch(toastOnFail("execute agent")), [api, graph, schedule, onForcedRun, toastOnFail], ); const runActions: ButtonAction[] = useMemo( - () => [{ label: "Run now", callback: () => runNow() }], + () => [ + { + label: ( + <> + + Run now + + ), + callback: runNow, + }, + { + label: ( + <> + + Delete schedule + + ), + callback: () => doDeleteSchedule(schedule.id), + variant: "destructive", + }, + ], [runNow], ); diff --git a/autogpt_platform/frontend/src/components/agents/agent-status-chip.tsx b/autogpt_platform/frontend/src/components/agents/agent-status-chip.tsx new file mode 100644 index 0000000000..3fd9fd7374 --- /dev/null +++ b/autogpt_platform/frontend/src/components/agents/agent-status-chip.tsx @@ -0,0 +1,40 @@ +import React from "react"; + +import { Badge } from "@/components/ui/badge"; + +export type AgentStatus = "active" | "inactive" | "error"; + +const statusData: Record< + AgentStatus, + { label: string; variant: keyof typeof statusStyles } +> = { + active: { label: "Active", variant: "success" }, + error: { label: "Error", variant: "destructive" }, + inactive: { label: "Inactive", variant: "secondary" }, +}; + +const statusStyles = { + success: + "bg-green-100 text-green-800 hover:bg-green-100 hover:text-green-800", + destructive: "bg-red-100 text-red-800 hover:bg-red-100 hover:text-red-800", + warning: + "bg-yellow-100 text-yellow-800 hover:bg-yellow-100 hover:text-yellow-800", + info: "bg-blue-100 text-blue-800 hover:bg-blue-100 hover:text-blue-800", + secondary: + "bg-slate-100 text-slate-800 hover:bg-slate-100 hover:text-slate-800", +}; + +export default function AgentStatusChip({ + status, +}: { + status: AgentStatus; +}): React.ReactElement { + return ( + + {statusData[status].label} + + ); +} diff --git a/autogpt_platform/frontend/src/components/agptui/AgentImages.stories.tsx b/autogpt_platform/frontend/src/components/agptui/AgentImages.stories.tsx deleted file mode 100644 index 8e0e3ba4d8..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/AgentImages.stories.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { AgentImages } from "./AgentImages"; - -const meta = { - title: "Legacy/Agent Images", - component: AgentImages, - parameters: { - layout: { - center: true, - fullscreen: true, - padding: 0, - }, - }, - tags: ["autodocs"], - argTypes: { - images: { control: "object" }, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - images: [ - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - "https://youtu.be/KWonAsyKF3g?si=JMibxlN_6OVo6LhJ", - "https://storage.googleapis.com/agpt-dev-website-media/DJINeo.mp4", - ], - }, -}; - -export const OnlyImages: Story = { - args: { - images: [ - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - "https://upload.wikimedia.org/wikipedia/commons/c/c5/Big_buck_bunny_poster_big.jpg", - ], - }, -}; - -export const WithVideos: Story = { - args: { - images: [ - "https://storage.googleapis.com/agpt-dev-website-media/DJINeo.mp4", - "http://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4", - "https://youtu.be/KWonAsyKF3g?si=JMibxlN_6OVo6LhJ", - ], - }, -}; - -export const SingleItem: Story = { - args: { - images: [ - "https://upload.wikimedia.org/wikipedia/commons/c/c5/Big_buck_bunny_poster_big.jpg", - ], - }, -}; diff --git a/autogpt_platform/frontend/src/components/agptui/AgentInfo.stories.tsx b/autogpt_platform/frontend/src/components/agptui/AgentInfo.stories.tsx deleted file mode 100644 index 06ecaef2f0..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/AgentInfo.stories.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { AgentInfo } from "./AgentInfo"; -import { userEvent, within } from "storybook/test"; - -const meta = { - title: "Legacy/Agent Info", - component: AgentInfo, - parameters: { - layout: "centered", - }, - tags: ["autodocs"], - argTypes: { - name: { control: "text" }, - creator: { control: "text" }, - shortDescription: { control: "text" }, - longDescription: { control: "text" }, - rating: { control: "number", min: 0, max: 5, step: 0.1 }, - runs: { control: "number" }, - categories: { control: "object" }, - lastUpdated: { control: "text" }, - version: { control: "text" }, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - user: null, - libraryAgent: null, - name: "AI Video Generator", - storeListingVersionId: "123", - creator: "Toran Richards", - shortDescription: - "Transform ideas into breathtaking images with this AI-powered Image Generator.", - longDescription: `Create Viral-Ready Content in Seconds! Transform trending topics into engaging videos with this cutting-edge AI Video Generator. Perfect for content creators, social media managers, and marketers looking to quickly produce high-quality content. - -Key features include: -- Customizable video output -- 15+ pre-made templates -- Auto scene detection -- Smart text-to-speech -- Multiple export formats -- SEO-optimized suggestions`, - rating: 4.7, - runs: 1500, - categories: ["Video", "Content Creation", "Social Media"], - lastUpdated: "2 days ago", - version: "1.2.0", - }, -}; - -export const LowRating: Story = { - args: { - ...Default.args, - name: "Data Analyzer", - creator: "DataTech", - shortDescription: - "Analyze complex datasets with machine learning algorithms", - longDescription: - "A comprehensive data analysis tool that leverages machine learning to provide deep insights into your datasets. Currently in beta testing phase.", - rating: 2.7, - runs: 5000, - categories: ["Data Analysis", "Machine Learning"], - lastUpdated: "1 week ago", - version: "0.9.5", - }, -}; - -export const HighRuns: Story = { - args: { - ...Default.args, - name: "Code Assistant", - creator: "DevAI", - shortDescription: - "Get AI-powered coding help for various programming languages", - longDescription: - "An advanced AI coding assistant that supports multiple programming languages and frameworks. Features include code completion, refactoring suggestions, and bug detection.", - rating: 4.8, - runs: 1000000, - categories: ["Programming", "AI", "Developer Tools"], - lastUpdated: "1 day ago", - version: "2.1.3", - }, -}; - -export const WithInteraction: Story = { - args: { - ...Default.args, - name: "Task Planner", - creator: "Productivity AI", - shortDescription: "Plan and organize your tasks efficiently with AI", - longDescription: - "An intelligent task management system that helps you organize, prioritize, and complete your tasks more efficiently. Features smart scheduling and AI-powered suggestions.", - rating: 4.2, - runs: 50000, - categories: ["Productivity", "Task Management", "AI"], - lastUpdated: "3 days ago", - version: "1.5.2", - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - - // Test run agent button - const runButton = canvas.getByText("Run agent"); - await userEvent.hover(runButton); - await userEvent.click(runButton); - - // Test rating interaction - const ratingStars = canvas.getAllByLabelText(/Star Icon/); - await userEvent.hover(ratingStars[3]); - await userEvent.click(ratingStars[3]); - - // Test category interaction - const category = canvas.getByText("Productivity"); - await userEvent.hover(category); - await userEvent.click(category); - }, -}; - -export const LongDescription: Story = { - args: { - ...Default.args, - name: "AI Writing Assistant", - creator: "WordCraft AI", - shortDescription: - "Enhance your writing with our advanced AI-powered assistant.", - longDescription: - "It offers real-time suggestions for grammar, style, and tone, helps with research and fact-checking, and can even generate content ideas based on your input.", - rating: 4.7, - runs: 75000, - categories: ["Writing", "AI", "Content Creation"], - lastUpdated: "5 days ago", - version: "3.0.1", - }, -}; diff --git a/autogpt_platform/frontend/src/components/agptui/AgentTable.stories.tsx b/autogpt_platform/frontend/src/components/agptui/AgentTable.stories.tsx deleted file mode 100644 index 992497edeb..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/AgentTable.stories.tsx +++ /dev/null @@ -1,108 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { AgentTable } from "./AgentTable"; -import { AgentTableRowProps } from "./AgentTableRow"; -import { userEvent, within, expect } from "storybook/test"; - -const meta: Meta = { - title: "Legacy/Agent Table", - component: AgentTable, - tags: ["autodocs"], -}; - -export default meta; -type Story = StoryObj; - -const sampleAgents: AgentTableRowProps[] = [ - { - id: 43, - agentName: "Super Coder", - description: "An AI agent that writes clean, efficient code", - imageSrc: [ - "https://ddz4ak4pa3d19.cloudfront.net/cache/53/b2/53b2bc7d7900f0e1e60bf64ebf38032d.jpg", - ], - dateSubmitted: "2023-05-15", - status: "approved", - runs: 1500, - rating: 4.8, - agent_id: "43", - agent_version: 1, - sub_heading: "Super Coder", - date_submitted: "2023-05-15", - onEditSubmission: () => console.log("Edit Super Coder"), - onDeleteSubmission: () => console.log("Delete Super Coder"), - selectedAgents: new Set(), - setSelectedAgents: () => {}, - }, - { - id: 44, - agentName: "Data Analyzer", - description: "Processes and analyzes large datasets with ease", - imageSrc: [ - "https://ddz4ak4pa3d19.cloudfront.net/cache/40/f7/40f7bc97c952f8df0f9c88d29defe8d4.jpg", - ], - dateSubmitted: "2023-05-10", - status: "awaiting_review", - runs: 1200, - rating: 4.5, - agent_id: "44", - agent_version: 1, - sub_heading: "Data Analyzer", - date_submitted: "2023-05-10", - onEditSubmission: () => console.log("Edit Data Analyzer"), - onDeleteSubmission: () => console.log("Delete Data Analyzer"), - selectedAgents: new Set(), - setSelectedAgents: () => {}, - }, - { - id: 45, - agentName: "UI Designer", - description: "Creates beautiful and intuitive user interfaces", - imageSrc: [ - "https://ddz4ak4pa3d19.cloudfront.net/cache/14/9e/149ebb9014aa8c0097e72ed89845af0e.jpg", - ], - dateSubmitted: "2023-05-05", - status: "draft", - runs: 800, - rating: 4.2, - agent_id: "45", - agent_version: 1, - sub_heading: "UI Designer", - date_submitted: "2023-05-05", - onEditSubmission: () => console.log("Edit UI Designer"), - onDeleteSubmission: () => console.log("Delete UI Designer"), - selectedAgents: new Set(), - setSelectedAgents: () => {}, - }, -]; - -export const Default: Story = { - args: { - agents: sampleAgents, - }, -}; - -export const EmptyTable: Story = { - args: { - agents: [], - }, -}; - -// Tests -export const InteractionTest: Story = { - ...Default, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const editButtons = await canvas.findAllByText("Edit"); - await userEvent.click(editButtons[0]); - // You would typically assert something here, but console.log is used in the mocked function - }, -}; - -export const EmptyTableTest: Story = { - ...EmptyTable, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const emptyMessage = canvas.getByText("No agents found"); - await expect(emptyMessage).toBeTruthy(); - }, -}; diff --git a/autogpt_platform/frontend/src/components/agptui/AgentTableCard.stories.tsx b/autogpt_platform/frontend/src/components/agptui/AgentTableCard.stories.tsx deleted file mode 100644 index b686e5c900..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/AgentTableCard.stories.tsx +++ /dev/null @@ -1,65 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { AgentTableCard } from "./AgentTableCard"; -import { userEvent, within } from "storybook/test"; -import { type StatusType } from "./Status"; - -const meta: Meta = { - title: "Legacy/Agent Table Card", - component: AgentTableCard, - tags: ["autodocs"], -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - agentName: "Super Coder", - description: "An AI agent that writes clean, efficient code", - imageSrc: [ - "https://ddz4ak4pa3d19.cloudfront.net/cache/53/b2/53b2bc7d7900f0e1e60bf64ebf38032d.jpg", - ], - dateSubmitted: "2023-05-15", - status: "ACTIVE" as StatusType, - runs: 1500, - rating: 4.8, - }, -}; - -export const NoRating: Story = { - args: { - ...Default.args, - rating: undefined, - }, -}; - -export const NoRuns: Story = { - args: { - ...Default.args, - runs: undefined, - }, -}; - -export const InactiveAgent: Story = { - args: { - ...Default.args, - status: "INACTIVE" as StatusType, - }, -}; - -export const LongDescription: Story = { - args: { - ...Default.args, - description: - "This is a very long description that should wrap to multiple lines. It contains detailed information about the agent and its capabilities.", - }, -}; - -export const InteractionTest: Story = { - ...Default, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const moreButton = canvas.getByRole("button"); - await userEvent.click(moreButton); - }, -}; diff --git a/autogpt_platform/frontend/src/components/agptui/BecomeACreator.stories.tsx b/autogpt_platform/frontend/src/components/agptui/BecomeACreator.stories.tsx deleted file mode 100644 index 0f5206c878..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/BecomeACreator.stories.tsx +++ /dev/null @@ -1,59 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { BecomeACreator } from "./BecomeACreator"; -import { userEvent, within } from "storybook/test"; - -const meta = { - title: "Legacy/Become A Creator", - component: BecomeACreator, - parameters: { - layout: "centered", - }, - tags: ["autodocs"], - argTypes: { - title: { control: "text" }, - description: { control: "text" }, - buttonText: { control: "text" }, - onButtonClick: { action: "buttonClicked" }, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - title: "Want to contribute?", - description: "Join our ever-growing community of hackers and tinkerers", - buttonText: "Become a Creator", - onButtonClick: () => console.log("Button clicked"), - }, -}; - -export const CustomText: Story = { - args: { - title: "Become a Creator Today!", - description: "Share your ideas and build amazing AI agents with us", - buttonText: "Start Creating", - onButtonClick: () => console.log("Custom button clicked"), - }, -}; - -export const LongDescription: Story = { - args: { - ...Default.args, - description: - "Join our vibrant community of innovators, developers, and AI enthusiasts. Share your unique perspectives, collaborate on groundbreaking projects, and help shape the future of AI technology.", - }, -}; - -export const WithInteraction: Story = { - args: { - ...Default.args, - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const button = canvas.getByText("Become a Creator"); - - await userEvent.click(button); - }, -}; diff --git a/autogpt_platform/frontend/src/components/agptui/BreadCrumbs.stories.tsx b/autogpt_platform/frontend/src/components/agptui/BreadCrumbs.stories.tsx deleted file mode 100644 index 55ff95498d..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/BreadCrumbs.stories.tsx +++ /dev/null @@ -1,79 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { BreadCrumbs } from "./BreadCrumbs"; -import { userEvent, within } from "storybook/test"; - -const meta = { - title: "Legacy/BreadCrumbs", - component: BreadCrumbs, - parameters: { - layout: "centered", - }, - tags: ["autodocs"], - argTypes: { - items: { control: "object" }, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - items: [ - { name: "Home", link: "/" }, - { name: "Agents", link: "/agents" }, - { name: "SEO Optimizer", link: "/agents/seo-optimizer" }, - ], - }, -}; - -export const SingleItem: Story = { - args: { - items: [{ name: "Home", link: "/" }], - }, -}; - -export const LongPath: Story = { - args: { - items: [ - { name: "Home", link: "/" }, - { name: "Categories", link: "/categories" }, - { name: "AI Tools", link: "/categories/ai-tools" }, - { name: "Data Analysis", link: "/categories/ai-tools/data-analysis" }, - { - name: "Data Analyzer", - link: "/categories/ai-tools/data-analysis/data-analyzer", - }, - ], - }, -}; - -export const WithInteraction: Story = { - args: { - items: [ - { name: "Home", link: "/" }, - { name: "Agents", link: "/agents" }, - { name: "Task Planner", link: "/agents/task-planner" }, - ], - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const homeLink = canvas.getByText("Home"); - - await userEvent.hover(homeLink); - await userEvent.click(homeLink); - }, -}; - -export const LongNames: Story = { - args: { - items: [ - { name: "Home", link: "/" }, - { name: "AI-Powered Writing Assistants", link: "/ai-writing-assistants" }, - { - name: "Advanced Grammar and Style Checker", - link: "/ai-writing-assistants/grammar-style-checker", - }, - ], - }, -}; diff --git a/autogpt_platform/frontend/src/components/agptui/Button.stories.tsx b/autogpt_platform/frontend/src/components/agptui/Button.stories.tsx deleted file mode 100644 index 6437c9e173..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/Button.stories.tsx +++ /dev/null @@ -1,217 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { Button } from "./Button"; -import { userEvent, within, expect } from "storybook/test"; - -const meta = { - title: "Legacy/Button", - component: Button, - parameters: { - layout: "centered", - }, - tags: ["autodocs"], - argTypes: { - variant: { - control: "select", - options: [ - "default", - "destructive", - "outline", - "secondary", - "ghost", - "link", - ], - }, - size: { - control: "select", - options: ["default", "sm", "lg", "primary", "icon"], - }, - disabled: { - control: "boolean", - }, - asChild: { - control: "boolean", - }, - children: { - control: "text", - }, - onClick: { action: "clicked" }, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - children: "Button", - }, -}; - -export const Interactive: Story = { - args: { - children: "Interactive Button", - }, - argTypes: { - onClick: { action: "clicked" }, - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const button = canvas.getByRole("button", { name: /Interactive Button/i }); - await userEvent.click(button); - await expect(button).toHaveFocus(); - }, -}; - -export const Variants: Story = { - render: (args) => ( -
- - - - - -
- ), - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const buttons = canvas.getAllByRole("button"); - await expect(buttons).toHaveLength(6); - for (const button of buttons) { - await userEvent.hover(button); - await expect(button).toHaveAttribute( - "class", - expect.stringContaining("hover:"), - ); - } - }, -}; -export const Sizes: Story = { - render: (args) => ( -
- - - - - -
- ), - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const buttons = canvas.getAllByRole("button"); - await expect(buttons).toHaveLength(5); - const sizeClasses = [ - "h-8 px-3 py-1.5 text-xs", - "h-10 px-4 py-2 text-sm", - "h-12 px-5 py-2.5 text-lg", - "h-10 w-28", - "h-10 w-10", - ]; - for (let i = 0; i < buttons.length; i++) { - await expect(buttons[i]).toHaveAttribute( - "class", - expect.stringContaining(sizeClasses[i]), - ); - } - }, -}; - -export const Disabled: Story = { - args: { - children: "Disabled Button", - disabled: true, - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const button = canvas.getByRole("button", { name: /Disabled Button/i }); - await expect(button).toBeDisabled(); - await expect(button).toHaveAttribute( - "class", - expect.stringContaining("disabled:opacity-50"), - ); - await expect(button).not.toHaveFocus(); - }, -}; - -export const WithIcon: Story = { - args: { - children: ( - <> - - - - Button with Icon - - ), - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const button = canvas.getByRole("button", { name: /Button with Icon/i }); - const icon = button.querySelector("svg"); - await expect(icon).toBeInTheDocument(); - await expect(button).toHaveTextContent("Button with Icon"); - }, -}; - -export const LoadingState: Story = { - args: { - children: "Loading...", - disabled: true, - }, - render: (args) => ( - - ), - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const button = canvas.getByRole("button", { name: /Loading.../i }); - await expect(button).toBeDisabled(); - const spinner = button.querySelector("svg"); - await expect(spinner).toHaveClass("animate-spin"); - }, -}; diff --git a/autogpt_platform/frontend/src/components/agptui/CreatorCard.stories.tsx b/autogpt_platform/frontend/src/components/agptui/CreatorCard.stories.tsx deleted file mode 100644 index 5f56309e6a..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/CreatorCard.stories.tsx +++ /dev/null @@ -1,77 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { CreatorCard } from "./CreatorCard"; -import { userEvent, within } from "storybook/test"; - -const meta = { - title: "Legacy/Creator Card", - component: CreatorCard, - parameters: { - layout: "centered", - }, - tags: ["autodocs"], - argTypes: { - creatorName: { control: "text" }, - creatorImage: { control: "text" }, - bio: { control: "text" }, - agentsUploaded: { control: "number" }, - onClick: { action: "clicked" }, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - index: 0, - creatorName: "John Doe", - creatorImage: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - bio: "AI enthusiast and developer with a passion for creating innovative agents.", - agentsUploaded: 15, - onClick: () => console.log("Default CreatorCard clicked"), - }, -}; - -export const NewCreator: Story = { - args: { - index: 1, - creatorName: "Jane Smith", - creatorImage: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - bio: "Excited to start my journey in AI agent development!", - agentsUploaded: 1, - onClick: () => console.log("NewCreator CreatorCard clicked"), - }, -}; - -export const ExperiencedCreator: Story = { - args: { - index: 2, - creatorName: "Alex Johnson", - creatorImage: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - bio: "Veteran AI researcher with a focus on natural language processing and machine learning.", - agentsUploaded: 50, - onClick: () => console.log("ExperiencedCreator CreatorCard clicked"), - }, -}; - -export const WithInteraction: Story = { - args: { - index: 3, - creatorName: "Sam Brown", - creatorImage: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - bio: "Exploring the frontiers of AI and its applications in everyday life.", - agentsUploaded: 30, - onClick: () => console.log("WithInteraction CreatorCard clicked"), - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const creatorCard = canvas.getByText("Sam Brown"); - - await userEvent.hover(creatorCard); - await userEvent.click(creatorCard); - }, -}; diff --git a/autogpt_platform/frontend/src/components/agptui/CreatorInfoCard.stories.tsx b/autogpt_platform/frontend/src/components/agptui/CreatorInfoCard.stories.tsx deleted file mode 100644 index d22715c9cb..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/CreatorInfoCard.stories.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { CreatorInfoCard } from "./CreatorInfoCard"; - -const meta = { - title: "Legacy/Creator Info Card", - component: CreatorInfoCard, - parameters: { - layout: "centered", - }, - tags: ["autodocs"], - argTypes: { - username: { control: "text" }, - handle: { control: "text" }, - avatarSrc: { control: "text" }, - categories: { control: "object" }, - averageRating: { control: "number", min: 0, max: 5, step: 0.1 }, - totalRuns: { control: "number" }, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - username: "SignificantGravitas", - handle: "oliviagrace1421", - avatarSrc: "https://github.com/shadcn.png", - categories: ["Entertainment", "Business"], - averageRating: 4.7, - totalRuns: 1500, - }, -}; - -export const NewCreator: Story = { - args: { - username: "AI Enthusiast", - handle: "ai_newbie", - avatarSrc: "https://example.com/avatar2.jpg", - categories: ["AI", "Technology"], - averageRating: 0, - totalRuns: 0, - }, -}; - -export const ExperiencedCreator: Story = { - args: { - username: "Tech Master", - handle: "techmaster", - avatarSrc: "https://example.com/avatar3.jpg", - categories: ["AI", "Development", "Education"], - averageRating: 4.9, - totalRuns: 50000, - }, -}; diff --git a/autogpt_platform/frontend/src/components/agptui/CreatorLinks.stories.tsx b/autogpt_platform/frontend/src/components/agptui/CreatorLinks.stories.tsx deleted file mode 100644 index 4f74571142..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/CreatorLinks.stories.tsx +++ /dev/null @@ -1,68 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { CreatorLinks } from "./CreatorLinks"; - -const meta = { - title: "Legacy/Creator Links", - component: CreatorLinks, - parameters: { - layout: "centered", - }, - tags: ["autodocs"], - argTypes: { - links: { - control: "object", - description: "Object containing various social and web links", - }, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - links: [ - "https://example.com", - "https://linkedin.com/in/johndoe", - "https://github.com/johndoe", - "https://twitter.com/johndoe", - "https://medium.com/@johndoe", - ], - }, -}; - -export const WebsiteOnly: Story = { - args: { - links: ["https://example.com"], - }, -}; - -export const SocialLinks: Story = { - args: { - links: [ - "https://linkedin.com/in/janedoe", - "https://github.com/janedoe", - "https://twitter.com/janedoe", - ], - }, -}; - -export const NoLinks: Story = { - args: { - links: [], - }, -}; - -export const MultipleOtherLinks: Story = { - args: { - links: [ - "https://example.com", - "https://linkedin.com/in/creator", - "https://github.com/creator", - "https://twitter.com/creator", - "https://medium.com/@creator", - "https://youtube.com/@creator", - "https://tiktok.com/@creator", - ], - }, -}; diff --git a/autogpt_platform/frontend/src/components/agptui/FeaturedStoreCard.stories.tsx b/autogpt_platform/frontend/src/components/agptui/FeaturedStoreCard.stories.tsx deleted file mode 100644 index e1ff9ac4fc..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/FeaturedStoreCard.stories.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { userEvent, within } from "storybook/test"; -import { FeaturedAgentCard } from "./FeaturedAgentCard"; - -const meta = { - title: "Legacy/Featured Store Card", - component: FeaturedAgentCard, - parameters: { - layout: { - center: true, - padding: 0, - }, - }, - tags: ["autodocs"], - argTypes: { - agent: { - agent_name: { control: "text" }, - sub_heading: { control: "text" }, - agent_image: { control: "text" }, - creator_avatar: { control: "text" }, - creator: { control: "text" }, - runs: { control: "number" }, - rating: { control: "number", min: 0, max: 5, step: 0.1 }, - slug: { control: "text" }, - }, - backgroundColor: { - control: "color", - }, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - agent: { - updated_at: "2024-01-10T15:30:00.000Z", - agent_name: - "Personalized Morning Coffee Newsletter example of three lines", - sub_heading: - "Transform ideas into breathtaking images with this AI-powered Image Generator.", - description: - "Elevate your web content with this powerful AI Webpage Copy Improver. Designed for marketers, SEO specialists, and web developers, this tool analyses and enhances website copy for maximum impact. Using advanced language models, it optimizes text for better clarity, SEO performance, and increased conversion rates.", - agent_image: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - creator_avatar: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - creator: "AI Solutions Inc.", - runs: 50000, - rating: 4.7, - slug: "", - }, - backgroundColor: "bg-white", - }, -}; - -export const WithInteraction: Story = { - args: { - agent: { - updated_at: "2024-01-10T15:30:00.000Z", - slug: "", - agent_name: "AI Writing Assistant", - sub_heading: "Enhance your writing", - description: - "An AI-powered writing assistant that helps improve your writing style and clarity.", - agent_image: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - creator_avatar: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - creator: "WordCraft AI", - runs: 200000, - rating: 4.6, - }, - backgroundColor: "bg-white", - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const featuredCard = canvas.getByTestId("featured-store-card"); - await userEvent.hover(featuredCard); - await userEvent.click(featuredCard); - }, -}; diff --git a/autogpt_platform/frontend/src/components/agptui/FilterChips.stories.tsx b/autogpt_platform/frontend/src/components/agptui/FilterChips.stories.tsx deleted file mode 100644 index 5258ad67ef..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/FilterChips.stories.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { FilterChips } from "./FilterChips"; -import { userEvent, within, expect } from "storybook/test"; - -const meta = { - title: "Legacy/Filter Chips", - component: FilterChips, - parameters: { - layout: "centered", - }, - tags: ["autodocs"], - argTypes: { - badges: { control: "object" }, - onFilterChange: { action: "onFilterChange" }, - multiSelect: { control: "boolean" }, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -const defaultBadges = [ - "Marketing", - "Sales", - "Content creation", - "Lorem ipsum", - "Lorem ipsum", -]; - -export const Default: Story = { - args: { - badges: defaultBadges, - multiSelect: true, - }, -}; - -export const SingleSelect: Story = { - args: { - badges: defaultBadges, - multiSelect: false, - }, -}; - -export const WithSelectedFilters: Story = { - args: { - badges: defaultBadges, - multiSelect: true, - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const marketingChip = canvas.getByText("Marketing").parentElement; - const salesChip = canvas.getByText("Sales").parentElement; - if (!marketingChip || !salesChip) { - throw new Error("Marketing or Sales chip not found"); - } - - await userEvent.click(marketingChip); - await userEvent.click(salesChip); - - await expect(marketingChip).toHaveClass("bg-neutral-100"); - await expect(salesChip).toHaveClass("bg-neutral-100"); - }, -}; - -export const WithFilterChangeCallback: Story = { - args: { - badges: defaultBadges, - multiSelect: true, - onFilterChange: (selectedFilters: string[]) => { - console.log("Selected filters:", selectedFilters); - }, - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const salesChip = canvas.getByText("Sales"); - const marketingChip = canvas.getByText("Marketing"); - - await userEvent.click(salesChip); - await userEvent.click(marketingChip); - }, -}; - -export const EmptyBadges: Story = { - args: { - badges: [], - multiSelect: true, - }, -}; - -export const LongBadgeNames: Story = { - args: { - badges: [ - "Machine Learning", - "Natural Language Processing", - "Computer Vision", - "Data Science", - ], - multiSelect: true, - }, -}; - -export const SingleSelectBehavior: Story = { - args: { - badges: defaultBadges, - multiSelect: false, - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const salesChip = canvas.getByText("Sales").parentElement; - const marketingChip = canvas.getByText("Marketing").parentElement; - - if (!salesChip || !marketingChip) { - throw new Error("Sales or Marketing chip not found"); - } - - await userEvent.click(salesChip); - await expect(salesChip).toHaveClass("bg-neutral-100"); - - await userEvent.click(marketingChip); - await expect(marketingChip).toHaveClass("bg-neutral-100"); - await expect(salesChip).not.toHaveClass("bg-neutral-100"); - }, -}; diff --git a/autogpt_platform/frontend/src/components/agptui/MobileNavBar.stories.tsx b/autogpt_platform/frontend/src/components/agptui/MobileNavBar.stories.tsx deleted file mode 100644 index 55d836d409..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/MobileNavBar.stories.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { MobileNavBar } from "./MobileNavBar"; -import { userEvent, within } from "storybook/test"; -import { IconType } from "../ui/icons"; - -const meta = { - title: "Legacy/Mobile Nav Bar", - component: MobileNavBar, - parameters: { - layout: "centered", - }, - tags: ["autodocs"], - argTypes: { - userName: { control: "text" }, - userEmail: { control: "text" }, - avatarSrc: { control: "text" }, - menuItemGroups: { control: "object" }, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -const defaultMenuItemGroups = [ - { - items: [ - { icon: IconType.Marketplace, text: "Marketplace", href: "/marketplace" }, - { icon: IconType.Library, text: "Library", href: "/library" }, - { icon: IconType.Builder, text: "Builder", href: "/builder" }, - ], - }, - { - items: [ - { icon: IconType.Edit, text: "Edit profile", href: "/profile/edit" }, - ], - }, - { - items: [ - { - icon: IconType.LayoutDashboard, - text: "Creator Dashboard", - href: "/dashboard", - }, - { - icon: IconType.UploadCloud, - text: "Publish an agent", - href: "/publish", - }, - ], - }, - { - items: [{ icon: IconType.Settings, text: "Settings", href: "/settings" }], - }, - { - items: [ - { - icon: IconType.LogOut, - text: "Log out", - onClick: () => console.log("Logged out"), - }, - ], - }, -]; - -export const Default: Story = { - args: { - userName: "John Doe", - userEmail: "john.doe@example.com", - avatarSrc: "https://avatars.githubusercontent.com/u/123456789?v=4", - menuItemGroups: defaultMenuItemGroups, - }, -}; - -export const NoAvatar: Story = { - args: { - userName: "Jane Smith", - userEmail: "jane.smith@example.com", - menuItemGroups: defaultMenuItemGroups, - }, -}; - -export const LongUserName: Story = { - args: { - userName: "Alexander Bartholomew Christopherson III", - userEmail: "alexander@example.com", - avatarSrc: "https://avatars.githubusercontent.com/u/987654321?v=4", - menuItemGroups: defaultMenuItemGroups, - }, -}; - -export const WithInteraction: Story = { - args: { - ...Default.args, - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const menuTrigger = canvas.getByRole("button"); - - await userEvent.click(menuTrigger); - - // Wait for the popover to appear - await canvas.findByText("Edit profile"); - }, -}; diff --git a/autogpt_platform/frontend/src/components/agptui/Navbar.stories.tsx b/autogpt_platform/frontend/src/components/agptui/Navbar.stories.tsx deleted file mode 100644 index 4c0b2243d1..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/Navbar.stories.tsx +++ /dev/null @@ -1,137 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { Navbar } from "./Navbar"; -import { userEvent, within } from "storybook/test"; -import { IconType } from "../ui/icons"; - -const meta = { - title: "Legacy/Navbar", - component: Navbar, - parameters: { - layout: "fullscreen", - }, - tags: ["autodocs"], - argTypes: { - // isLoggedIn: { control: "boolean" }, - // avatarSrc: { control: "text" }, - links: { control: "object" }, - // activeLink: { control: "text" }, - menuItemGroups: { control: "object" }, - // params: { control: { type: "object", defaultValue: { lang: "en" } } }, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -const defaultMenuItemGroups = [ - { - items: [ - { icon: IconType.Edit, text: "Edit profile", href: "/profile/edit" }, - ], - }, - { - items: [ - { - icon: IconType.LayoutDashboard, - text: "Creator Dashboard", - href: "/dashboard", - }, - { - icon: IconType.UploadCloud, - text: "Publish an agent", - href: "/publish", - }, - ], - }, - { - items: [{ icon: IconType.Settings, text: "Settings", href: "/settings" }], - }, - { - items: [ - { - icon: IconType.LogOut, - text: "Log out", - onClick: () => console.log("Logged out"), - }, - ], - }, -]; - -const defaultLinks = [ - { name: "Marketplace", href: "/marketplace" }, - { name: "Library", href: "/library" }, - { name: "Build", href: "/builder" }, -]; - -export const Default: Story = { - args: { - // params: { lang: "en" }, - // isLoggedIn: true, - links: defaultLinks, - // activeLink: "/marketplace", - // avatarSrc: mockProfileData.avatar_url, - menuItemGroups: defaultMenuItemGroups, - }, -}; - -export const WithActiveLink: Story = { - args: { - ...Default.args, - // activeLink: "/library", - }, -}; - -export const LongUserName: Story = { - args: { - ...Default.args, - // avatarSrc: "https://avatars.githubusercontent.com/u/987654321?v=4", - }, -}; - -export const NoAvatar: Story = { - args: { - ...Default.args, - // avatarSrc: undefined, - }, -}; - -export const WithInteraction: Story = { - args: { - ...Default.args, - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const profileTrigger = canvas.getByRole("button"); - - await userEvent.click(profileTrigger); - - // Wait for the popover to appear - await canvas.findByText("Edit profile"); - }, -}; - -export const NotLoggedIn: Story = { - args: { - ...Default.args, - // isLoggedIn: false, - // avatarSrc: undefined, - }, -}; - -export const WithCredits: Story = { - args: { - ...Default.args, - }, -}; - -export const WithLargeCredits: Story = { - args: { - ...Default.args, - }, -}; - -export const WithZeroCredits: Story = { - args: { - ...Default.args, - }, -}; diff --git a/autogpt_platform/frontend/src/components/agptui/ProfileInfoForm.stories.tsx b/autogpt_platform/frontend/src/components/agptui/ProfileInfoForm.stories.tsx deleted file mode 100644 index 2c43c58e57..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/ProfileInfoForm.stories.tsx +++ /dev/null @@ -1,73 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { ProfileInfoForm } from "./ProfileInfoForm"; - -const meta: Meta = { - title: "Legacy/Profile/Profile Info Form", - component: ProfileInfoForm, - parameters: { - layout: "fullscreen", - }, - tags: ["autodocs"], - argTypes: { - profile: { - control: "object", - description: "The profile details of the user", - displayName: { - control: "text", - description: "The display name of the user", - }, - handle: { - control: "text", - description: "The user's handle/username", - }, - bio: { - control: "text", - description: "User's biography text", - }, - profileImage: { - control: "text", - description: "URL of the user's profile image", - }, - links: { - control: "object", - description: "Array of social media links", - }, - categories: { - control: "object", - description: "Array of selected categories", - }, - }, - }, -}; - -export default meta; -type Story = StoryObj; - -export const Empty: Story = { - args: { - profile: { - name: "", - username: "", - description: "", - avatar_url: "", - links: [], - }, - }, -}; - -export const Filled: Story = { - args: { - profile: { - name: "Olivia Grace", - username: "@ograce1421", - description: - "Our agents are designed to bring happiness and positive vibes to your daily routine. Each template helps you create and live more efficiently.", - avatar_url: "https://via.placeholder.com/130x130", - links: [ - "www.websitelink.com", - "twitter.com/oliviagrace", - "github.com/ograce", - ], - }, - }, -}; diff --git a/autogpt_platform/frontend/src/components/agptui/ProfileInfoForm.tsx b/autogpt_platform/frontend/src/components/agptui/ProfileInfoForm.tsx index ad45fd93b7..c27035b622 100644 --- a/autogpt_platform/frontend/src/components/agptui/ProfileInfoForm.tsx +++ b/autogpt_platform/frontend/src/components/agptui/ProfileInfoForm.tsx @@ -1,24 +1,21 @@ "use client"; -import * as React from "react"; import { useState } from "react"; import Image from "next/image"; -import { Button } from "./Button"; import { IconPersonFill } from "@/components/ui/icons"; -import { ProfileDetails } from "@/lib/autogpt-server-api/types"; import { Separator } from "@/components/ui/separator"; -import { useSupabase } from "@/lib/supabase/hooks/useSupabase"; import { useBackendAPI } from "@/lib/autogpt-server-api/context"; +import { ProfileDetails } from "@/lib/autogpt-server-api/types"; +import { Button } from "./Button"; -export const ProfileInfoForm = ({ profile }: { profile: ProfileDetails }) => { +export function ProfileInfoForm({ profile }: { profile: ProfileDetails }) { const [isSubmitting, setIsSubmitting] = useState(false); const [profileData, setProfileData] = useState(profile); - const { supabase } = useSupabase(); const api = useBackendAPI(); - const submitForm = async () => { + async function submitForm() { try { setIsSubmitting(true); @@ -39,48 +36,12 @@ export const ProfileInfoForm = ({ profile }: { profile: ProfileDetails }) => { } finally { setIsSubmitting(false); } - }; + } - const handleImageUpload = async (file: File) => { + async function handleImageUpload(file: File) { try { - // Create FormData and append file - const formData = new FormData(); - formData.append("file", file); + const mediaUrl = await api.uploadStoreSubmissionMedia(file); - // Get auth token - if (!supabase) { - throw new Error("Supabase client not initialized"); - } - - const { - data: { session }, - } = await supabase.auth.getSession(); - const token = session?.access_token; - - if (!token) { - throw new Error("No authentication token found"); - } - - // Make upload request - const response = await fetch( - `${process.env.NEXT_PUBLIC_AGPT_SERVER_URL}/store/submissions/media`, - { - method: "POST", - headers: { - Authorization: `Bearer ${token}`, - }, - body: formData, - }, - ); - - if (!response.ok) { - throw new Error(`Upload failed: ${response.statusText}`); - } - - // Get media URL from response - const mediaUrl = await response.json(); - - // Update profile with new avatar URL const updatedProfile = { ...profileData, avatar_url: mediaUrl, @@ -91,7 +52,7 @@ export const ProfileInfoForm = ({ profile }: { profile: ProfileDetails }) => { } catch (error) { console.error("Error uploading image:", error); } - }; + } return (
@@ -261,4 +222,4 @@ export const ProfileInfoForm = ({ profile }: { profile: ProfileDetails }) => {
); -}; +} diff --git a/autogpt_platform/frontend/src/components/agptui/ProfilePopoutMenu.stories.tsx b/autogpt_platform/frontend/src/components/agptui/ProfilePopoutMenu.stories.tsx deleted file mode 100644 index d86a23d322..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/ProfilePopoutMenu.stories.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { ProfilePopoutMenu } from "./ProfilePopoutMenu"; -import { userEvent, within } from "storybook/test"; -import { IconType } from "../ui/icons"; - -const meta = { - title: "Legacy/Profile Popout Menu", - component: ProfilePopoutMenu, - parameters: { - layout: "centered", - }, - tags: ["autodocs"], - argTypes: { - userName: { control: "text" }, - userEmail: { control: "text" }, - avatarSrc: { control: "text" }, - menuItemGroups: { control: "object" }, - hideNavBarUsername: { control: "boolean" }, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -const defaultMenuItemGroups = [ - { - // Creator actions group - items: [ - { - icon: IconType.LayoutDashboard, - text: "Creator Dashboard", - href: "/dashboard", - }, - { - icon: IconType.UploadCloud, - text: "Publish an agent", - href: "/publish", - }, - ], - }, - { - // Profile management group - items: [ - { icon: IconType.Edit, text: "Edit profile", href: "/profile/edit" }, - { icon: IconType.Settings, text: "Settings", href: "/settings" }, - ], - }, - { - // Logout group - items: [ - { - icon: IconType.LogOut, - text: "Log out", - onClick: () => console.log("Logged out"), - }, - ], - }, -]; - -export const Default: Story = { - args: { - userName: "John Doe", - userEmail: "john.doe@example.com", - avatarSrc: "https://avatars.githubusercontent.com/u/123456789?v=4", - menuItemGroups: defaultMenuItemGroups, - }, -}; - -export const NoAvatar: Story = { - args: { - userName: "Jane Smith", - userEmail: "jane.smith@example.com", - menuItemGroups: defaultMenuItemGroups, - }, -}; - -export const LongUserName: Story = { - args: { - userName: "Alexander Bartholomew Christopherson III", - userEmail: "alexander@example.com", - avatarSrc: "https://avatars.githubusercontent.com/u/987654321?v=4", - menuItemGroups: defaultMenuItemGroups, - }, -}; - -export const WithInteraction: Story = { - args: { - ...Default.args, - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const profileTrigger = canvas.getByText("John Doe"); - - await userEvent.click(profileTrigger); - - // Wait for the popover to appear - await canvas.findByText("Edit profile"); - }, -}; diff --git a/autogpt_platform/frontend/src/components/agptui/PublishAgentAwaitingReview.stories.tsx b/autogpt_platform/frontend/src/components/agptui/PublishAgentAwaitingReview.stories.tsx deleted file mode 100644 index 4302af9009..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/PublishAgentAwaitingReview.stories.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { PublishAgentAwaitingReview } from "./PublishAgentAwaitingReview"; - -const meta: Meta = { - title: "Legacy/Publish Agent Awaiting Review", - component: PublishAgentAwaitingReview, - tags: ["autodocs"], - parameters: { - layout: "centered", - }, -}; - -export default meta; -type Story = StoryObj; - -export const Filled: Story = { - args: { - agentName: "AI Video Generator", - subheader: "Create Viral-Ready Content in Seconds", - description: - "AI Shortform Video Generator: Create Viral-Ready Content in Seconds Transform trending topics into engaging shortform videos with this cutting-edge AI Video Generator. Perfect for content creators, social media managers, and marketers looking to capitalize on the latest news and viral trends. Simply input your desired video count and source website, and watch as the AI scours the internet for the hottest stories, crafting them into attention-grabbing scripts optimized for platforms like TikTok, Instagram Reels, and YouTube Shorts. Key features include: - Customizable video count (1-5 per generation) - Flexible source selection for trending topics - AI-driven script writing following best practices for shortform content - Hooks that capture attention in the first 3 seconds - Dual narrative storytelling for maximum engagement - SEO-optimized content to boost discoverability - Integration with video generation tools for seamless production From hook to conclusion, each script is meticulously crafted to maintain viewer interest, incorporating proven techniques like 'but so' storytelling, visual metaphors, and strategically placed calls-to-action. The AI Shortform Video Generator streamlines your content creation process, allowing you to stay ahead of trends and consistently produce viral-worthy videos that resonate with your audience.", - thumbnailSrc: "https://picsum.photos/seed/video/500/350", - onClose: () => console.log("Close clicked"), - onDone: () => console.log("Done clicked"), - onViewProgress: () => console.log("View progress clicked"), - }, -}; diff --git a/autogpt_platform/frontend/src/components/agptui/PublishAgentAwaitingReview.tsx b/autogpt_platform/frontend/src/components/agptui/PublishAgentAwaitingReview.tsx index 496c6ae303..07b792af1f 100644 --- a/autogpt_platform/frontend/src/components/agptui/PublishAgentAwaitingReview.tsx +++ b/autogpt_platform/frontend/src/components/agptui/PublishAgentAwaitingReview.tsx @@ -1,7 +1,7 @@ "use client"; import * as React from "react"; -import { IconClose } from "../ui/icons"; +import { IconCross } from "../ui/icons"; import Image from "next/image"; import { Button } from "../agptui/Button"; @@ -50,7 +50,7 @@ export const PublishAgentAwaitingReview: React.FC< className="absolute right-4 top-4 flex h-[38px] w-[38px] items-center justify-center rounded-full bg-gray-100 transition-colors hover:bg-gray-200 dark:bg-neutral-700 dark:hover:bg-neutral-600" aria-label="Close dialog" > - diff --git a/autogpt_platform/frontend/src/components/agptui/PublishAgentSelect.stories.tsx b/autogpt_platform/frontend/src/components/agptui/PublishAgentSelect.stories.tsx deleted file mode 100644 index c116e66db9..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/PublishAgentSelect.stories.tsx +++ /dev/null @@ -1,119 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { Agent, PublishAgentSelect } from "./PublishAgentSelect"; - -const meta: Meta = { - title: "Legacy/Publish Agent Select", - component: PublishAgentSelect, - tags: ["autodocs"], -}; - -export default meta; -type Story = StoryObj; - -const mockAgents: Agent[] = [ - { - name: "SEO Optimizer", - lastEdited: "2 days ago", - imageSrc: "https://picsum.photos/seed/seo/300/200", - id: "1", - version: 1, - }, - { - name: "Content Writer", - lastEdited: "5 days ago", - imageSrc: "https://picsum.photos/seed/writer/300/200", - id: "1", - version: 1, - }, - { - name: "Data Analyzer", - lastEdited: "1 week ago", - imageSrc: "https://picsum.photos/seed/data/300/200", - id: "1", - version: 1, - }, - { - name: "Image Recognition", - lastEdited: "2 weeks ago", - imageSrc: "https://picsum.photos/seed/image/300/200", - id: "1", - version: 1, - }, - { - name: "Chatbot Assistant", - lastEdited: "3 weeks ago", - imageSrc: "https://picsum.photos/seed/chat/300/200", - id: "1", - version: 1, - }, - { - name: "Code Generator", - lastEdited: "1 month ago", - imageSrc: "https://picsum.photos/seed/code/300/200", - id: "1", - version: 1, - }, - { - name: "AI Translator", - lastEdited: "6 weeks ago", - imageSrc: "https://picsum.photos/seed/translate/300/200", - id: "1", - version: 1, - }, - { - name: "Voice Assistant", - lastEdited: "2 months ago", - imageSrc: "https://picsum.photos/seed/voice/300/200", - id: "1", - version: 1, - }, - { - name: "Data Visualizer", - lastEdited: "3 months ago", - imageSrc: "https://picsum.photos/seed/visualize/300/200", - id: "1", - version: 1, - }, -]; - -const defaultArgs = { - onSelect: (agentName: string) => console.log(`Selected: ${agentName}`), - onCancel: () => console.log("Cancelled"), - onNext: () => console.log("Next clicked"), - onOpenBuilder: () => console.log("Open builder clicked"), -}; - -export const Default: Story = { - args: { - ...defaultArgs, - agents: mockAgents, - }, -}; - -export const NoAgents: Story = { - args: { - ...defaultArgs, - agents: [], - }, -}; - -export const SingleAgent: Story = { - args: { - ...defaultArgs, - agents: [mockAgents[0]], - }, -}; - -export const SixAgents: Story = { - args: { - ...defaultArgs, - agents: mockAgents.slice(0, 6), - }, -}; - -export const NineAgents: Story = { - args: { - ...defaultArgs, - agents: mockAgents, - }, -}; diff --git a/autogpt_platform/frontend/src/components/agptui/PublishAgentSelect.tsx b/autogpt_platform/frontend/src/components/agptui/PublishAgentSelect.tsx index 56ffe0f0bd..f89d6a856e 100644 --- a/autogpt_platform/frontend/src/components/agptui/PublishAgentSelect.tsx +++ b/autogpt_platform/frontend/src/components/agptui/PublishAgentSelect.tsx @@ -3,7 +3,7 @@ import * as React from "react"; import Image from "next/image"; import { Button } from "../agptui/Button"; -import { IconClose } from "../ui/icons"; +import { IconCross } from "../ui/icons"; export interface Agent { name: string; @@ -56,7 +56,7 @@ export const PublishAgentSelect: React.FC = ({ className="flex h-8 w-8 items-center justify-center rounded-full bg-gray-100 transition-colors hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600" aria-label="Close" > - diff --git a/autogpt_platform/frontend/src/components/agptui/PublishAgentSelectInfo.stories.tsx b/autogpt_platform/frontend/src/components/agptui/PublishAgentSelectInfo.stories.tsx deleted file mode 100644 index 621af7a8c2..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/PublishAgentSelectInfo.stories.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { PublishAgentInfo } from "./PublishAgentSelectInfo"; - -const meta: Meta = { - title: "Legacy/Publish Agent Info", - component: PublishAgentInfo, - tags: ["autodocs"], - decorators: [ - (Story) => ( -
- -
- ), - ], -}; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - onBack: () => console.log("Back clicked"), - onSubmit: () => console.log("Submit clicked"), - onClose: () => console.log("Close clicked"), - }, -}; - -export const Filled: Story = { - args: { - ...Default.args, - initialData: { - agent_id: "1", - slug: "super-seo-optimizer", - title: "Super SEO Optimizer", - subheader: "Boost your website's search engine rankings", - thumbnailSrc: "https://picsum.photos/seed/seo/500/350", - youtubeLink: "https://www.youtube.com/watch?v=dQw4w9WgXcQ", - category: "SEO", - description: - "This AI agent specializes in analyzing websites and providing actionable recommendations to improve search engine optimization. It can perform keyword research, analyze backlinks, and suggest content improvements.", - }, - }, -}; - -export const ThreeImages: Story = { - args: { - ...Default.args, - initialData: { - agent_id: "1", - slug: "super-seo-optimizer", - title: "Multi-Image Agent", - subheader: "Showcasing multiple images", - thumbnailSrc: "https://picsum.photos/seed/initial/500/350", - youtubeLink: "https://www.youtube.com/watch?v=dQw4w9WgXcQ", - category: "SEO", - description: - "This agent allows you to upload and manage multiple images.", - additionalImages: [ - "https://picsum.photos/seed/second/500/350", - "https://picsum.photos/seed/third/500/350", - ], - }, - }, -}; - -export const SixImages: Story = { - args: { - ...Default.args, - initialData: { - agent_id: "1", - slug: "super-seo-optimizer", - title: "Gallery Agent", - subheader: "Showcasing a gallery of images", - thumbnailSrc: "https://picsum.photos/seed/gallery1/500/350", - youtubeLink: "https://www.youtube.com/watch?v=dQw4w9WgXcQ", - category: "SEO", - description: "This agent displays a gallery of six images.", - additionalImages: [ - "https://picsum.photos/seed/gallery2/500/350", - "https://picsum.photos/seed/gallery3/500/350", - "https://picsum.photos/seed/gallery4/500/350", - "https://picsum.photos/seed/gallery5/500/350", - "https://picsum.photos/seed/gallery6/500/350", - ], - }, - }, -}; diff --git a/autogpt_platform/frontend/src/components/agptui/PublishAgentSelectInfo.tsx b/autogpt_platform/frontend/src/components/agptui/PublishAgentSelectInfo.tsx index 4a64647487..bd9630af63 100644 --- a/autogpt_platform/frontend/src/components/agptui/PublishAgentSelectInfo.tsx +++ b/autogpt_platform/frontend/src/components/agptui/PublishAgentSelectInfo.tsx @@ -3,7 +3,7 @@ import * as React from "react"; import Image from "next/image"; import { Button } from "../agptui/Button"; -import { IconClose, IconPlus } from "../ui/icons"; +import { IconCross, IconPlus } from "../ui/icons"; import BackendAPI from "@/lib/autogpt-server-api"; import { toast } from "../ui/use-toast"; @@ -180,7 +180,7 @@ export const PublishAgentInfo: React.FC = ({ className="flex h-[38px] w-[38px] items-center justify-center rounded-full bg-gray-100 transition-colors hover:bg-gray-200 dark:bg-gray-700 dark:hover:bg-gray-600" aria-label="Close" > - @@ -313,7 +313,7 @@ export const PublishAgentInfo: React.FC = ({ className="absolute right-1 top-1 flex h-5 w-5 items-center justify-center rounded-full bg-white bg-opacity-70 transition-opacity hover:bg-opacity-100 dark:bg-gray-800 dark:bg-opacity-70 dark:hover:bg-opacity-100" aria-label="Remove image" > - diff --git a/autogpt_platform/frontend/src/components/agptui/RatingCard.stories.tsx b/autogpt_platform/frontend/src/components/agptui/RatingCard.stories.tsx deleted file mode 100644 index 7e2b70c443..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/RatingCard.stories.tsx +++ /dev/null @@ -1,47 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { RatingCard } from "./RatingCard"; - -const meta = { - title: "Legacy/RatingCard", - component: RatingCard, - parameters: { - layout: "centered", - }, - tags: ["autodocs"], -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - agentName: "Test Agent", - // onSubmit: (rating) => { - // console.log("Rating submitted:", rating); - // }, - // onClose: () => { - // console.log("Rating card closed"); - // }, - storeListingVersionId: "1", - }, -}; - -export const LongAgentName: Story = { - args: { - agentName: "Very Long Agent Name That Might Need Special Handling", - // onSubmit: (rating) => { - // console.log("Rating submitted:", rating); - // }, - // onClose: () => { - // console.log("Rating card closed"); - // }, - storeListingVersionId: "1", - }, -}; - -export const WithoutCallbacks: Story = { - args: { - agentName: "Test Agent", - storeListingVersionId: "1", - }, -}; diff --git a/autogpt_platform/frontend/src/components/agptui/SearchBar.stories.tsx b/autogpt_platform/frontend/src/components/agptui/SearchBar.stories.tsx deleted file mode 100644 index c5b1df8a7b..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/SearchBar.stories.tsx +++ /dev/null @@ -1,86 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { SearchBar } from "./SearchBar"; -import { userEvent, within, expect } from "storybook/test"; - -const meta = { - title: "Legacy/Search Bar", - component: SearchBar, - parameters: { - layout: { - center: true, - padding: 0, - }, - nextjs: { - appDirectory: true, - navigation: { - pathname: "/search", - query: { - searchTerm: "", - }, - }, - }, - }, - tags: ["autodocs"], - argTypes: { - placeholder: { control: "text" }, - backgroundColor: { control: "text" }, - iconColor: { control: "text" }, - textColor: { control: "text" }, - placeholderColor: { control: "text" }, - }, - decorators: [ - (Story) => ( -
- -
- ), - ], -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - placeholder: 'Search for tasks like "optimise SEO"', - }, -}; - -export const CustomStyles: Story = { - args: { - placeholder: "Enter your search query", - backgroundColor: "bg-blue-100", - iconColor: "text-blue-500", - textColor: "text-blue-700", - placeholderColor: "text-blue-400", - }, -}; - -export const WithInteraction: Story = { - args: { - placeholder: "Type and press Enter", - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const input = canvas.getByPlaceholderText("Type and press Enter"); - - await userEvent.type(input, "test query"); - await userEvent.keyboard("{Enter}"); - - await expect(input).toHaveValue("test query"); - }, -}; - -export const EmptySubmit: Story = { - args: { - placeholder: "Empty submit test", - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const input = canvas.getByPlaceholderText("Empty submit test"); - - await userEvent.keyboard("{Enter}"); - - await expect(input).toHaveValue(""); - }, -}; diff --git a/autogpt_platform/frontend/src/components/agptui/Sidebar.stories.tsx b/autogpt_platform/frontend/src/components/agptui/Sidebar.stories.tsx deleted file mode 100644 index e59e54c384..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/Sidebar.stories.tsx +++ /dev/null @@ -1,34 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { Sidebar } from "./Sidebar"; - -const meta = { - title: "Legacy/Sidebar", - component: Sidebar, - parameters: { - layout: "centered", - }, - tags: ["autodocs"], - argTypes: { - linkGroups: { control: "object" }, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -const defaultLinkGroups = [ - { - links: [ - { text: "Agent dashboard", href: "/dashboard" }, - { text: "Integrations", href: "/integrations" }, - { text: "Profile", href: "/profile" }, - { text: "Settings", href: "/settings" }, - ], - }, -]; - -export const Default: Story = { - args: { - linkGroups: defaultLinkGroups, - }, -}; diff --git a/autogpt_platform/frontend/src/components/agptui/Status.stories.tsx b/autogpt_platform/frontend/src/components/agptui/Status.stories.tsx deleted file mode 100644 index cdfb38540d..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/Status.stories.tsx +++ /dev/null @@ -1,58 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { Status, StatusType } from "./Status"; - -const meta = { - title: "Legacy/Status", - component: Status, - parameters: { - layout: "centered", - }, - tags: ["autodocs"], - argTypes: { - status: { - control: "select", - options: ["draft", "awaiting_review", "approved", "rejected"], - }, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Draft: Story = { - args: { - status: "draft" as StatusType, - }, -}; - -export const AwaitingReview: Story = { - args: { - status: "awaiting_review" as StatusType, - }, -}; - -export const Approved: Story = { - args: { - status: "approved" as StatusType, - }, -}; - -export const Rejected: Story = { - args: { - status: "rejected" as StatusType, - }, -}; - -export const AllStatuses: Story = { - args: { - status: "draft" as StatusType, - }, - render: () => ( -
- - - - -
- ), -}; diff --git a/autogpt_platform/frontend/src/components/agptui/StoreCard.stories.tsx b/autogpt_platform/frontend/src/components/agptui/StoreCard.stories.tsx deleted file mode 100644 index 956b25b8b2..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/StoreCard.stories.tsx +++ /dev/null @@ -1,115 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { StoreCard } from "./StoreCard"; -import { userEvent, within } from "storybook/test"; - -const meta = { - title: "Legacy/StoreCard", - component: StoreCard, - parameters: { - layout: { - center: true, - fullscreen: true, - padding: 0, - }, - }, - tags: ["autodocs"], - argTypes: { - agentName: { control: "text" }, - agentImage: { control: "text" }, - description: { control: "text" }, - runs: { control: "number" }, - rating: { control: "number", min: 0, max: 5, step: 0.1 }, - onClick: { action: "clicked" }, - avatarSrc: { control: "text" }, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - agentName: "SEO Optimizer", - agentImage: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - description: "Optimize your website's SEO with AI-powered suggestions", - runs: 10000, - rating: 4.5, - onClick: () => console.log("Default StoreCard clicked"), - avatarSrc: "https://github.com/shadcn.png", - }, -}; - -export const LowRating: Story = { - args: { - agentName: "Data Analyzer", - agentImage: - "https://upload.wikimedia.org/wikipedia/commons/c/c5/Big_buck_bunny_poster_big.jpg", - description: "Analyze complex datasets with machine learning algorithms", - runs: 5000, - rating: 2.7, - onClick: () => console.log("LowRating StoreCard clicked"), - avatarSrc: "https://example.com/avatar2.jpg", - }, -}; - -export const HighRuns: Story = { - args: { - agentName: "Code Assistant", - agentImage: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - description: "Get AI-powered coding help for various programming languages", - runs: 1000000, - rating: 4.8, - onClick: () => console.log("HighRuns StoreCard clicked"), - avatarSrc: "https://example.com/avatar3.jpg", - }, -}; - -export const WithInteraction: Story = { - args: { - agentName: "Task Planner", - agentImage: - "https://upload.wikimedia.org/wikipedia/commons/c/c5/Big_buck_bunny_poster_big.jpg", - description: "Plan and organize your tasks efficiently with AI", - runs: 50000, - rating: 4.2, - onClick: () => console.log("WithInteraction StoreCard clicked"), - avatarSrc: "https://example.com/avatar4.jpg", - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const storeCard = canvas.getByText("Task Planner"); - - await userEvent.hover(storeCard); - await userEvent.click(storeCard); - }, -}; - -export const LongDescription: Story = { - args: { - agentName: "AI Writing Assistant", - agentImage: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - description: - "Enhance your writing with our advanced AI-powered assistant. It offers real-time suggestions for grammar, style, and tone, helps with research and fact-checking.", - runs: 75000, - rating: 4.7, - onClick: () => console.log("LongDescription StoreCard clicked"), - avatarSrc: "https://example.com/avatar5.jpg", - }, -}; - -export const HiddenAvatar: Story = { - args: { - agentName: "Data Visualizer", - agentImage: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - description: "Create stunning visualizations from complex datasets", - runs: 60000, - rating: 4.6, - onClick: () => console.log("HiddenAvatar StoreCard clicked"), - avatarSrc: "https://example.com/avatar6.jpg", - hideAvatar: true, - }, -}; diff --git a/autogpt_platform/frontend/src/components/agptui/composite/APIKeySection.tsx b/autogpt_platform/frontend/src/components/agptui/composite/APIKeySection.tsx deleted file mode 100644 index 1fa9db3cdc..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/composite/APIKeySection.tsx +++ /dev/null @@ -1,296 +0,0 @@ -"use client"; - -import { useState, useEffect } from "react"; - -import { APIKey, APIKeyPermission } from "@/lib/autogpt-server-api/types"; - -import { LuCopy } from "react-icons/lu"; -import { Loader2, MoreVertical } from "lucide-react"; -import { useBackendAPI } from "@/lib/autogpt-server-api/context"; -import { useToast } from "@/components/ui/use-toast"; -import { - Card, - CardContent, - CardDescription, - CardHeader, - CardTitle, -} from "@/components/ui/card"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog"; -import { Button } from "@/components/ui/button"; -import { Label } from "@/components/ui/label"; -import { Input } from "@/components/ui/input"; -import { Checkbox } from "@/components/ui/checkbox"; -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from "@/components/ui/table"; -import { Badge } from "@/components/ui/badge"; -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu"; - -export function APIKeysSection() { - const [apiKeys, setApiKeys] = useState([]); - const [isLoading, setIsLoading] = useState(true); - const [isCreateOpen, setIsCreateOpen] = useState(false); - const [isKeyDialogOpen, setIsKeyDialogOpen] = useState(false); - const [newKeyName, setNewKeyName] = useState(""); - const [newKeyDescription, setNewKeyDescription] = useState(""); - const [newApiKey, setNewApiKey] = useState(""); - const [selectedPermissions, setSelectedPermissions] = useState< - APIKeyPermission[] - >([]); - const { toast } = useToast(); - const api = useBackendAPI(); - - useEffect(() => { - loadAPIKeys(); - }, []); - - const loadAPIKeys = async () => { - setIsLoading(true); - try { - const keys = await api.listAPIKeys(); - setApiKeys(keys.filter((key) => key.status === "ACTIVE")); - } finally { - setIsLoading(false); - } - }; - - const handleCreateKey = async () => { - try { - const response = await api.createAPIKey( - newKeyName, - selectedPermissions, - newKeyDescription, - ); - - setNewApiKey(response.plain_text_key); - setIsCreateOpen(false); - setIsKeyDialogOpen(true); - loadAPIKeys(); - } catch { - toast({ - title: "Error", - description: "Failed to create AutoGPT Platform API key", - variant: "destructive", - }); - } - }; - - const handleCopyKey = () => { - navigator.clipboard.writeText(newApiKey); - toast({ - title: "Copied", - description: "AutoGPT Platform API key copied to clipboard", - }); - }; - - const handleRevokeKey = async (keyId: string) => { - try { - await api.revokeAPIKey(keyId); - toast({ - title: "Success", - description: "AutoGPT Platform API key revoked successfully", - }); - loadAPIKeys(); - } catch { - toast({ - title: "Error", - description: "Failed to revoke AutoGPT Platform API key", - variant: "destructive", - }); - } - }; - - return ( - - - AutoGPT Platform API Keys - - Manage your AutoGPT Platform API keys for programmatic access - - - -
- - - - - - - Create New API Key - - Create a new AutoGPT Platform API key - - -
-
- - setNewKeyName(e.target.value)} - placeholder="My AutoGPT Platform API Key" - /> -
-
- - setNewKeyDescription(e.target.value)} - placeholder="Used for..." - /> -
-
- - {Object.values(APIKeyPermission).map((permission) => ( -
- { - setSelectedPermissions( - checked - ? [...selectedPermissions, permission] - : selectedPermissions.filter( - (p) => p !== permission, - ), - ); - }} - /> - -
- ))} -
-
- - - - -
-
- - - - - AutoGPT Platform API Key Created - - Please copy your AutoGPT API key now. You won't be able - to see it again! - - -
- - {newApiKey} - - -
- - - -
-
-
- - {isLoading ? ( -
- -
- ) : ( - apiKeys.length > 0 && ( - - - - Name - API Key - Status - Created - Last Used - - - - - {apiKeys.map((key) => ( - - {key.name} - -
- {`${key.prefix}******************${key.postfix}`} -
-
- - - {key.status} - - - - {new Date(key.created_at).toLocaleDateString()} - - - {key.last_used_at - ? new Date(key.last_used_at).toLocaleDateString() - : "Never"} - - - - - - - - handleRevokeKey(key.id)} - > - Revoke - - - - -
- ))} -
-
- ) - )} -
-
- ); -} diff --git a/autogpt_platform/frontend/src/components/agptui/composite/AgentsSection.stories.tsx b/autogpt_platform/frontend/src/components/agptui/composite/AgentsSection.stories.tsx deleted file mode 100644 index a9313ce9d8..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/composite/AgentsSection.stories.tsx +++ /dev/null @@ -1,199 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { Agent, AgentsSection } from "./AgentsSection"; -import { userEvent, within, expect } from "storybook/test"; - -const meta = { - title: "Legacy/Composite/Agents Section", - component: AgentsSection, - parameters: { - layout: { - center: true, - fullscreen: true, - padding: 0, - }, - }, - tags: ["autodocs"], - argTypes: { - sectionTitle: { control: "text" }, - agents: { control: "object" }, - // onCardClick: { action: "clicked" }, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -const mockTopAgents = [ - { - agent_name: "SEO Optimizer Pro", - agent_image: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - description: - "Boost your website's search engine rankings with our advanced AI-powered SEO optimization tool.", - runs: 50000, - rating: 4.7, - creator_avatar: "https://example.com/avatar1.jpg", - slug: "seo-optimizer-pro", - creator: "John Doe", - sub_heading: "SEO Expert", - }, - { - agent_name: "Content Writer AI", - agent_image: - "https://upload.wikimedia.org/wikipedia/commons/c/c5/Big_buck_bunny_poster_big.jpg", - description: - "Generate high-quality, engaging content for your blog, social media, or marketing campaigns.", - runs: 75000, - rating: 4.5, - creator_avatar: "https://example.com/avatar2.jpg", - slug: "content-writer-ai", - creator: "Jane Doe", - sub_heading: "Content Writer", - }, - { - agent_name: "Data Analyzer Lite", - agent_image: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - description: "A basic tool for analyzing small to medium-sized datasets.", - runs: 10000, - rating: 3.8, - creator_avatar: "https://example.com/avatar3.jpg", - slug: "data-analyzer-lite", - creator: "John Doe", - sub_heading: "Data Analyst", - }, -] satisfies Agent[]; - -export const Default: Story = { - args: { - sectionTitle: "Top Agents", - agents: mockTopAgents, - // onCardClick: (agentName: string) => console.log(`Clicked on ${agentName}`), - }, -}; - -export const SingleAgent: Story = { - args: { - sectionTitle: "Top Agents", - agents: [mockTopAgents[0]], - // onCardClick: (agentName: string) => console.log(`Clicked on ${agentName}`), - }, -}; - -export const NoAgents: Story = { - args: { - sectionTitle: "Top Agents", - agents: [], - // onCardClick: (agentName: string) => console.log(`Clicked on ${agentName}`), - }, -}; - -export const WithInteraction: Story = { - args: { - sectionTitle: "Top Agents", - agents: mockTopAgents, - // onCardClick: (agentName: string) => console.log(`Clicked on ${agentName}`), - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const firstCard = canvas.getAllByRole("store-card")[0]; - await userEvent.click(firstCard); - await expect(firstCard).toHaveAttribute("aria-pressed", "true"); - }, -}; - -export const MultiRowAgents: Story = { - args: { - sectionTitle: "Top Agents", - agents: [ - ...mockTopAgents, - { - agent_name: "Image Recognition AI", - agent_image: - "https://upload.wikimedia.org/wikipedia/commons/c/c5/Big_buck_bunny_poster_big.jpg", - description: - "Accurately identify and classify objects in images using state-of-the-art machine learning algorithms.", - runs: 60000, - rating: 4.6, - creator_avatar: "https://example.com/avatar4.jpg", - slug: "image-recognition-ai", - creator: "John Doe", - sub_heading: "Image Recognition", - }, - { - agent_name: "Natural Language Processor", - agent_image: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - description: - "Analyze and understand human language with advanced NLP techniques.", - runs: 80000, - rating: 4.8, - creator_avatar: "https://example.com/avatar5.jpg", - slug: "natural-language-processor", - creator: "John Doe", - sub_heading: "Natural Language Processing", - }, - { - agent_name: "Sentiment Analyzer", - agent_image: - "https://upload.wikimedia.org/wikipedia/commons/c/c5/Big_buck_bunny_poster_big.jpg", - description: - "Determine the emotional tone of text data for customer feedback analysis.", - runs: 45000, - rating: 4.3, - creator_avatar: "https://example.com/avatar6.jpg", - slug: "sentiment-analyzer", - creator: "John Doe", - sub_heading: "Sentiment Analysis", - }, - { - agent_name: "Chatbot Builder", - agent_image: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - description: - "Create intelligent chatbots for customer service and engagement.", - runs: 55000, - rating: 4.4, - creator_avatar: "https://example.com/avatar7.jpg", - slug: "chatbot-builder", - creator: "John Doe", - sub_heading: "Chatbot Developer", - }, - { - agent_name: "Predictive Analytics Tool", - agent_image: - "https://upload.wikimedia.org/wikipedia/commons/c/c5/Big_buck_bunny_poster_big.jpg", - description: - "Forecast future trends and outcomes based on historical data.", - runs: 40000, - rating: 4.2, - creator_avatar: "https://example.com/avatar8.jpg", - slug: "predictive-analytics-tool", - creator: "John Doe", - sub_heading: "Predictive Analytics", - }, - { - agent_name: "Text-to-Speech Converter", - agent_image: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - description: - "Convert written text into natural-sounding speech in multiple languages.", - runs: 35000, - rating: 4.1, - creator_avatar: "https://example.com/avatar9.jpg", - slug: "text-to-speech-converter", - creator: "John Doe", - sub_heading: "Text-to-Speech", - }, - ], - // onCardClick: (agentName: string) => console.log(`Clicked on ${agentName}`), - }, -}; - -export const HiddenAvatars: Story = { - args: { - ...Default.args, - hideAvatars: true, - sectionTitle: "Agents with Hidden Avatars", - }, -}; diff --git a/autogpt_platform/frontend/src/components/agptui/composite/FeaturedCreators.stories.tsx b/autogpt_platform/frontend/src/components/agptui/composite/FeaturedCreators.stories.tsx deleted file mode 100644 index cec11cb00c..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/composite/FeaturedCreators.stories.tsx +++ /dev/null @@ -1,121 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { FeaturedCreators } from "./FeaturedCreators"; -import { userEvent, within, expect } from "storybook/test"; - -const meta = { - title: "Legacy/Composite/Featured Creators", - component: FeaturedCreators, - parameters: { - layout: { - center: true, - fullscreen: true, - padding: 0, - }, - }, - tags: ["autodocs"], - argTypes: { - featuredCreators: { control: "object" }, - // onCardClick: { action: "cardClicked" }, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -const defaultCreators = [ - { - name: "AI Innovator", - username: "ai_innovator", - description: - "Pushing the boundaries of AI technology with cutting-edge solutions and innovative approaches to machine learning.", - avatar_url: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - num_agents: 15, - }, - { - name: "Code Wizard", - username: "code_wizard", - description: - "Crafting elegant solutions with AI and helping others learn the magic of coding.", - avatar_url: - "https://upload.wikimedia.org/wikipedia/commons/c/c5/Big_buck_bunny_poster_big.jpg", - num_agents: 8, - }, - { - name: "Data Alchemist", - username: "data_alchemist", - description: - "Transforming raw data into AI gold. Specializing in data processing and analytics.", - avatar_url: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - num_agents: 12, - }, - { - name: "ML Master", - username: "ml_master", - description: - "Specializing in machine learning algorithms and neural network architectures.", - avatar_url: - "https://upload.wikimedia.org/wikipedia/commons/c/c5/Big_buck_bunny_poster_big.jpg", - num_agents: 20, - }, -]; - -export const Default: Story = { - args: { - featuredCreators: defaultCreators, - // onCardClick: (creatorName) => console.log(`Clicked on ${creatorName}`), - }, -}; - -export const SingleCreator: Story = { - args: { - featuredCreators: [defaultCreators[0]], - // onCardClick: (creatorName) => console.log(`Clicked on ${creatorName}`), - }, -}; - -export const ManyCreators: Story = { - args: { - featuredCreators: [ - ...defaultCreators, - { - name: "NLP Ninja", - username: "nlp_ninja", - description: - "Expert in natural language processing and text analysis systems.", - avatar_url: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - num_agents: 18, - }, - { - name: "AI Explorer", - username: "ai_explorer", - description: - "Discovering new frontiers in artificial intelligence and autonomous systems.", - avatar_url: - "https://upload.wikimedia.org/wikipedia/commons/c/c5/Big_buck_bunny_poster_big.jpg", - num_agents: 25, - }, - ], - // onCardClick: (creatorName) => console.log(`Clicked on ${creatorName}`), - }, -}; - -export const WithInteraction: Story = { - args: { - featuredCreators: defaultCreators, - // onCardClick: (creatorName) => console.log(`Clicked on ${creatorName}`), - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const creatorCards = canvas.getAllByRole("creator-card"); - const firstCreatorCard = creatorCards[0]; - - await userEvent.hover(firstCreatorCard); - await userEvent.click(firstCreatorCard); - - // Check if the card has the expected hover and click effects - await expect(firstCreatorCard).toHaveClass("hover:shadow-lg"); - }, -}; diff --git a/autogpt_platform/frontend/src/components/agptui/composite/FeaturedSection.stories.tsx b/autogpt_platform/frontend/src/components/agptui/composite/FeaturedSection.stories.tsx deleted file mode 100644 index dcb2aa81a8..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/composite/FeaturedSection.stories.tsx +++ /dev/null @@ -1,138 +0,0 @@ -import { StoreAgent } from "@/lib/autogpt-server-api"; -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { userEvent, within } from "storybook/test"; -import { FeaturedSection } from "./FeaturedSection"; - -const meta = { - title: "Legacy/Composite/Featured Agents", - component: FeaturedSection, - parameters: { - layout: { - center: true, - fullscreen: true, - padding: 0, - }, - }, - tags: ["autodocs"], - argTypes: { - featuredAgents: { control: "object" }, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -const mockFeaturedAgents = [ - { - updated_at: "2024-01-10T15:30:00.000Z", - agent_name: "Personalized Morning Coffee Newsletter example of three lines", - sub_heading: - "Transform ideas into breathtaking images with this AI-powered Image Generator.", - creator: "AI Solutions Inc.", - description: - "Elevate your web content with this powerful AI Webpage Copy Improver. Designed for marketers, SEO specialists, and web developers, this tool analyses and enhances website copy for maximum impact. Using advanced language models, it optimizes text for better clarity, SEO performance, and increased conversion rates.", - runs: 50000, - rating: 4.7, - agent_image: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - creator_avatar: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - slug: "personalized-morning-coffee-newsletter", - }, - { - updated_at: "2024-01-10T15:30:00.000Z", - agent_name: "Data Analyzer Lite", - sub_heading: "Basic data analysis tool", - creator: "DataTech", - description: - "A lightweight data analysis tool for basic data processing needs.", - runs: 10000, - rating: 2.8, - agent_image: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - creator_avatar: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - slug: "data-analyzer-lite", - }, - { - updated_at: "2024-01-10T15:30:00.000Z", - agent_name: "CodeAssist AI", - sub_heading: "Your AI coding companion", - creator: "DevTools Co.", - description: - "An intelligent coding assistant that helps developers write better code faster.", - runs: 1000000, - rating: 4.9, - agent_image: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - creator_avatar: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - slug: "codeassist-ai", - }, - { - updated_at: "2024-01-10T15:30:00.000Z", - agent_name: "MultiTasker", - sub_heading: "All-in-one productivity suite", - creator: "Productivity Plus", - description: - "A comprehensive productivity suite that combines task management, note-taking, and project planning into one seamless interface. Features include smart task prioritization, automated scheduling, and AI-powered insights to help you work more efficiently.", - runs: 75000, - rating: 4.5, - agent_image: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - creator_avatar: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - slug: "multitasker", - }, - { - updated_at: "2024-01-10T15:30:00.000Z", - agent_name: "QuickTask", - sub_heading: "Fast task automation", - creator: "EfficientWorks", - description: "Simple and efficient task automation tool.", - runs: 50000, - rating: 4.2, - agent_image: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - creator_avatar: - "https://framerusercontent.com/images/KCIpxr9f97EGJgpaoqnjKsrOPwI.jpg", - slug: "quicktask", - }, -] satisfies StoreAgent[]; - -export const Default: Story = { - args: { - featuredAgents: mockFeaturedAgents, - // onCardClick: (agentName: string) => console.log(`Clicked on ${agentName}`), - }, -}; - -export const SingleAgent: Story = { - args: { - featuredAgents: [mockFeaturedAgents[0]], - // onCardClick: (agentName: string) => console.log(`Clicked on ${agentName}`), - }, -}; - -export const NoAgents: Story = { - args: { - featuredAgents: [], - // onCardClick: (agentName: string) => console.log(`Clicked on ${agentName}`), - }, -}; - -export const WithInteraction: Story = { - args: { - featuredAgents: mockFeaturedAgents, - // onCardClick: (agentName: string) => console.log(`Clicked on ${agentName}`), - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const featuredCard = canvas.getByText( - "Personalized Morning Coffee Newsletter example of three lines", - ); - - await userEvent.hover(featuredCard); - await userEvent.click(featuredCard); - }, -}; diff --git a/autogpt_platform/frontend/src/components/agptui/composite/HeroSection.stories.tsx b/autogpt_platform/frontend/src/components/agptui/composite/HeroSection.stories.tsx deleted file mode 100644 index 6a0add5dcc..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/composite/HeroSection.stories.tsx +++ /dev/null @@ -1,70 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { HeroSection } from "./HeroSection"; -import { userEvent, within, expect } from "storybook/test"; - -const meta = { - title: "Legacy/Composite/Hero Section", - component: HeroSection, - parameters: { - layout: { - center: true, - fullscreen: true, - padding: 0, - }, - }, - tags: ["autodocs"], - argTypes: { - onSearch: { action: "searched" }, - onFilterChange: { action: "filtersChanged" }, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: { - onSearch: (query: string) => console.log(`Searched: ${query}`), - onFilterChange: (selectedFilters: string[]) => - console.log(`Filters changed: ${selectedFilters.join(", ")}`), - }, -}; - -export const WithInteraction: Story = { - args: { - onSearch: (query: string) => console.log(`Searched: ${query}`), - onFilterChange: (selectedFilters: string[]) => - console.log(`Filters changed: ${selectedFilters.join(", ")}`), - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const searchInput = canvas.getByRole("store-search-input"); - - await userEvent.type(searchInput, "test query"); - await userEvent.keyboard("{Enter}"); - - await expect(searchInput).toHaveValue("test query"); - - const filterChip = canvas.getByText("Marketing"); - await userEvent.click(filterChip); - - await expect(filterChip).toHaveClass("text-[#474747]"); - }, -}; - -export const EmptySearch: Story = { - args: { - onSearch: (query: string) => console.log(`Searched: ${query}`), - onFilterChange: (selectedFilters: string[]) => - console.log(`Filters changed: ${selectedFilters.join(", ")}`), - }, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - const searchInput = canvas.getByRole("store-search-input"); - - await userEvent.click(searchInput); - await userEvent.keyboard("{Enter}"); - - await expect(searchInput).toHaveValue(""); - }, -}; diff --git a/autogpt_platform/frontend/src/components/agptui/composite/PublishAgentPopout.stories.tsx b/autogpt_platform/frontend/src/components/agptui/composite/PublishAgentPopout.stories.tsx deleted file mode 100644 index c06f1274df..0000000000 --- a/autogpt_platform/frontend/src/components/agptui/composite/PublishAgentPopout.stories.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import type { Meta, StoryObj } from "@storybook/nextjs"; -import { PublishAgentPopout } from "@/components/agptui/composite/PublishAgentPopout"; -import { userEvent, within } from "storybook/test"; - -const meta = { - title: "Legacy/Composite/Publish Agent Popout", - component: PublishAgentPopout, - parameters: { - layout: "centered", - }, - tags: ["autodocs"], - argTypes: { - trigger: { control: "object" }, - }, -} satisfies Meta; - -export default meta; -type Story = StoryObj; - -export const Default: Story = { - args: {}, -}; - -export const WithCustomTrigger: Story = { - args: { - trigger: , - }, -}; - -export const PublishFlow: Story = { - args: {}, - play: async ({ canvasElement }) => { - const canvas = within(canvasElement); - - // Open popout - const publishButton = canvas.getByText("Publish Agent"); - await userEvent.click(publishButton); - - // Select an agent (assuming one exists) - const agentCard = await canvas.findByRole("button", { - name: /select agent/i, - }); - await userEvent.click(agentCard); - - // Click next - const nextButton = canvas.getByText("Next"); - await userEvent.click(nextButton); - - // Fill out info form - // Note: Actual form interactions would need to be added based on PublishAgentInfo implementation - }, -}; diff --git a/autogpt_platform/frontend/src/components/atoms/Badge/Badge.stories.tsx b/autogpt_platform/frontend/src/components/atoms/Badge/Badge.stories.tsx new file mode 100644 index 0000000000..c502a85f3c --- /dev/null +++ b/autogpt_platform/frontend/src/components/atoms/Badge/Badge.stories.tsx @@ -0,0 +1,77 @@ +import type { Meta, StoryObj } from "@storybook/nextjs"; +import { Badge } from "./Badge"; + +const meta: Meta = { + title: "Atoms/Badge", + tags: ["autodocs"], + component: Badge, + parameters: { + layout: "centered", + docs: { + description: { + component: + "Badge component for displaying status information with different variants for success, error, and info states.", + }, + }, + }, + argTypes: { + variant: { + control: "select", + options: ["success", "error", "info"], + description: "Badge variant that determines color scheme", + }, + children: { + control: "text", + description: "Badge content", + }, + className: { + control: "text", + description: "Additional CSS classes", + }, + }, + args: { + variant: "success", + children: "Success", + }, +}; + +export default meta; +type Story = StoryObj; + +export const Success: Story = { + args: { + variant: "success", + children: "Success", + }, +}; + +export const Error: Story = { + args: { + variant: "error", + children: "Failed", + }, +}; + +export const Info: Story = { + args: { + variant: "info", + children: "Stopped", + }, +}; + +export const AllVariants: Story = { + render: renderAllVariants, +}; + +function renderAllVariants() { + return ( +
+ Success + Failed + Stopped + Running + Completed + Error +
+ ); +} diff --git a/autogpt_platform/frontend/src/components/atoms/Badge/Badge.test.tsx b/autogpt_platform/frontend/src/components/atoms/Badge/Badge.test.tsx new file mode 100644 index 0000000000..cd8531375b --- /dev/null +++ b/autogpt_platform/frontend/src/components/atoms/Badge/Badge.test.tsx @@ -0,0 +1,81 @@ +// import { render, screen } from "@testing-library/react"; +// import { describe, expect, it } from "vitest"; +// import { Badge } from "./Badge"; + +// describe("Badge Component", () => { +// it("renders badge with content", () => { +// render(Success); + +// expect(screen.getByText("Success")).toBeInTheDocument(); +// }); + +// it("applies correct variant styles", () => { +// const { rerender } = render(Success); +// let badge = screen.getByText("Success"); +// expect(badge).toHaveClass("bg-green-100", "text-green-800"); + +// rerender(Error); +// badge = screen.getByText("Error"); +// expect(badge).toHaveClass("bg-red-100", "text-red-800"); + +// rerender(Info); +// badge = screen.getByText("Info"); +// expect(badge).toHaveClass("bg-slate-100", "text-slate-800"); +// }); + +// it("applies custom className", () => { +// render( +// +// Success +// , +// ); + +// const badge = screen.getByText("Success"); +// expect(badge).toHaveClass("custom-class"); +// }); + +// it("renders as span element", () => { +// render(Success); + +// const badge = screen.getByText("Success"); +// expect(badge.tagName).toBe("SPAN"); +// }); + +// it("renders children correctly", () => { +// render( +// +// Custom Content +// , +// ); + +// expect(screen.getByText("Custom")).toBeInTheDocument(); +// expect(screen.getByText("Content")).toBeInTheDocument(); +// }); + +// it("supports all badge variants", () => { +// const variants = ["success", "error", "info"] as const; + +// variants.forEach((variant) => { +// const { unmount } = render( +// +// {variant} +// , +// ); + +// expect(screen.getByTestId(`badge-${variant}`)).toBeInTheDocument(); +// unmount(); +// }); +// }); + +// it("handles long text content", () => { +// render( +// +// Very long text that should be handled properly by the component +// , +// ); + +// const badge = screen.getByText(/Very long text/); +// expect(badge).toBeInTheDocument(); +// expect(badge).toHaveClass("overflow-hidden", "text-ellipsis"); +// }); +// }); diff --git a/autogpt_platform/frontend/src/components/atoms/Badge/Badge.tsx b/autogpt_platform/frontend/src/components/atoms/Badge/Badge.tsx new file mode 100644 index 0000000000..33e8ca6ab0 --- /dev/null +++ b/autogpt_platform/frontend/src/components/atoms/Badge/Badge.tsx @@ -0,0 +1,35 @@ +import { cn } from "@/lib/utils"; + +type BadgeVariant = "success" | "error" | "info"; + +interface BadgeProps { + variant: BadgeVariant; + children: React.ReactNode; + className?: string; +} + +const badgeVariants: Record = { + success: "bg-green-100 text-green-800", + error: "bg-red-100 text-red-800", + info: "bg-slate-100 text-slate-800", +}; + +export function Badge({ variant, children, className }: BadgeProps) { + return ( + + {children} + + ); +} diff --git a/autogpt_platform/frontend/src/components/atoms/Button/Button.stories.tsx b/autogpt_platform/frontend/src/components/atoms/Button/Button.stories.tsx index 9b3be42fe8..3c6765e26c 100644 --- a/autogpt_platform/frontend/src/components/atoms/Button/Button.stories.tsx +++ b/autogpt_platform/frontend/src/components/atoms/Button/Button.stories.tsx @@ -94,26 +94,48 @@ export const Ghost: Story = { }, }; -export const Link: Story = { - args: { - variant: "link", - children: "Add to library", - }, -}; - // Loading states export const Loading: Story = { args: { loading: true, - children: "Processing...", + children: "Saving...", + }, + parameters: { + docs: { + description: { + story: + "Use contextual loading text that reflects the action being performed (e.g., 'Computing...', 'Processing...', 'Saving...', 'Uploading...', 'Deleting...')", + }, + }, }, }; -export const LoadingSecondary: Story = { +export const LoadingGhost: Story = { args: { - variant: "secondary", + variant: "ghost", loading: true, - children: "Loading...", + children: "Fetching data...", + }, + parameters: { + docs: { + description: { + story: + "Always show contextual loading text that describes what's happening. Avoid generic 'Loading...' text when possible.", + }, + }, + }, +}; + +// Contextual loading examples +export const ContextualLoadingExamples: Story = { + render: renderContextualLoadingExamples, + parameters: { + docs: { + description: { + story: + "Examples of contextual loading text. Always use specific action-based text rather than generic 'Loading...' to give users clear feedback about what's happening.", + }, + }, }, }; @@ -163,6 +185,65 @@ export const AllVariants: Story = { }; // Render functions as function declarations +function renderContextualLoadingExamples() { + return ( +
+
+

+ ✅ Good Examples - Contextual Loading Text +

+
+ + + + + + + + +
+
+ +
+

+ ❌ Avoid - Generic Loading Text +

+
+ + + +
+

+ These examples are disabled to show what NOT to do. Use specific + action-based text instead. +

+
+
+ ); +} + function renderSmallButtons() { return (
@@ -405,6 +486,9 @@ function renderAllVariants() { + @@ -427,6 +511,9 @@ function renderAllVariants() { + @@ -449,6 +536,9 @@ function renderAllVariants() { + @@ -471,6 +561,9 @@ function renderAllVariants() { + @@ -492,17 +585,6 @@ function renderAllVariants() { Other button types
- {/* Link */} -
-
- Link -
-
- -
-
- - {/* Icon */}
Icon diff --git a/autogpt_platform/frontend/src/components/atoms/Button/Button.tsx b/autogpt_platform/frontend/src/components/atoms/Button/Button.tsx index 556b41c052..c829e1afd2 100644 --- a/autogpt_platform/frontend/src/components/atoms/Button/Button.tsx +++ b/autogpt_platform/frontend/src/components/atoms/Button/Button.tsx @@ -19,7 +19,6 @@ const extendedButtonVariants = cva( "bg-transparent border-zinc-700 text-black hover:bg-zinc-100 hover:border-zinc-700 rounded-full disabled:border-zinc-200 disabled:text-zinc-200 disabled:opacity-1", ghost: "bg-transparent border-transparent text-black hover:bg-zinc-50 hover:border-zinc-50 rounded-full disabled:text-zinc-200 disabled:opacity-1", - link: "bg-transparent border-transparent text-black hover:underline rounded-none p-0 h-auto min-w-auto disabled:opacity-1", icon: "bg-white text-black border border-zinc-600 hover:bg-zinc-100 rounded-[96px] disabled:opacity-1", }, size: { @@ -57,6 +56,30 @@ function Button({ }: ButtonProps) { const isDisabled = disabled; + if (loading) { + return variant === "ghost" ? ( + + ) : ( + + ); + } + return ( + +
+
+ +
+ ); +} diff --git a/autogpt_platform/frontend/src/components/cron-scheduler.tsx b/autogpt_platform/frontend/src/components/cron-scheduler.tsx new file mode 100644 index 0000000000..2ffc8a0857 --- /dev/null +++ b/autogpt_platform/frontend/src/components/cron-scheduler.tsx @@ -0,0 +1,338 @@ +import React, { useEffect, useState } from "react"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Label } from "@/components/ui/label"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import { CronFrequency, makeCronExpression } from "@/lib/cron-expression-utils"; + +const weekDays = [ + { label: "Su", value: 0 }, + { label: "Mo", value: 1 }, + { label: "Tu", value: 2 }, + { label: "We", value: 3 }, + { label: "Th", value: 4 }, + { label: "Fr", value: 5 }, + { label: "Sa", value: 6 }, +]; + +const months = [ + { label: "Jan", value: "January" }, + { label: "Feb", value: "February" }, + { label: "Mar", value: "March" }, + { label: "Apr", value: "April" }, + { label: "May", value: "May" }, + { label: "Jun", value: "June" }, + { label: "Jul", value: "July" }, + { label: "Aug", value: "August" }, + { label: "Sep", value: "September" }, + { label: "Oct", value: "October" }, + { label: "Nov", value: "November" }, + { label: "Dec", value: "December" }, +]; + +type CronSchedulerProps = { + onCronExpressionChange: (cronExpression: string) => void; +}; + +export function CronScheduler({ + onCronExpressionChange, +}: CronSchedulerProps): React.ReactElement { + const [frequency, setFrequency] = useState("daily"); + const [selectedMinute, setSelectedMinute] = useState("0"); + const [selectedTime, setSelectedTime] = useState("09:00"); + const [selectedWeekDays, setSelectedWeekDays] = useState([]); + const [selectedMonthDays, setSelectedMonthDays] = useState([]); + const [selectedMonths, setSelectedMonths] = useState([]); + const [customInterval, setCustomInterval] = useState<{ + value: number; + unit: "minutes" | "hours" | "days"; + }>({ value: 1, unit: "minutes" }); + const [showCustomDays, setShowCustomDays] = useState(false); + + useEffect(() => { + const cronExpr = makeCronExpression({ + frequency, + minute: + frequency === "hourly" + ? parseInt(selectedMinute) + : parseInt(selectedTime.split(":")[1]), + hour: parseInt(selectedTime.split(":")[0]), + days: + frequency === "weekly" + ? selectedWeekDays + : frequency === "monthly" + ? selectedMonthDays + : [], + months: frequency === "yearly" ? selectedMonths : [], + customInterval: + frequency === "custom" ? customInterval : { unit: "minutes", value: 1 }, + }); + onCronExpressionChange(cronExpr); + }, [ + frequency, + selectedMinute, + selectedTime, + selectedWeekDays, + selectedMonthDays, + selectedMonths, + customInterval, + onCronExpressionChange, + ]); + + return ( +
+
+ + + + + {frequency === "hourly" && ( +
+ + +
+ )} + + {frequency === "custom" && ( +
+ + + setCustomInterval({ + ...customInterval, + value: parseInt(e.target.value), + }) + } + /> + +
+ )} +
+ + {frequency === "weekly" && ( +
+
+ + + + +
+
+ {weekDays.map((day) => ( + + ))} +
+
+ )} + {frequency === "monthly" && ( +
+ +
+ + + + +
+ {showCustomDays && ( +
+ {Array.from({ length: 31 }, (_, i) => ( + + ))} +
+ )} +
+ )} + {frequency === "yearly" && ( +
+ +
+ +
+
+ {months.map((month, i) => { + const monthNumber = i + 1; + return ( + + ); + })} +
+
+ )} + + {frequency !== "every minute" && frequency !== "hourly" && ( +
+ + setSelectedTime(e.target.value)} + /> +
+ )} +
+ ); +} diff --git a/autogpt_platform/frontend/src/components/cronScheduler.tsx b/autogpt_platform/frontend/src/components/cronScheduler.tsx deleted file mode 100644 index 37022fb171..0000000000 --- a/autogpt_platform/frontend/src/components/cronScheduler.tsx +++ /dev/null @@ -1,417 +0,0 @@ -import { useState } from "react"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "@/components/ui/select"; -import { Label } from "@/components/ui/label"; -import { Input } from "@/components/ui/input"; -import { Button } from "@/components/ui/button"; -import { Dialog, DialogContent, DialogTitle } from "@/components/ui/dialog"; -import { Separator } from "./ui/separator"; -import { CronExpressionManager } from "@/lib/monitor/cronExpressionManager"; - -interface CronSchedulerProps { - setOpen: React.Dispatch>; - open: boolean; - afterCronCreation: (cronExpression: string) => void; -} - -export function CronScheduler({ - setOpen, - open, - afterCronCreation, -}: CronSchedulerProps) { - const [frequency, setFrequency] = useState< - "minute" | "hour" | "daily" | "weekly" | "monthly" | "yearly" | "custom" - >("daily"); - const [selectedDays, setSelectedDays] = useState([]); - const [selectedTime, setSelectedTime] = useState("09:00"); - const [showCustomDays, setShowCustomDays] = useState(false); - const [selectedMinute, setSelectedMinute] = useState("0"); - const [customInterval, setCustomInterval] = useState<{ - value: number; - unit: "minutes" | "hours" | "days"; - }>({ value: 1, unit: "minutes" }); - - // const [endType, setEndType] = useState<"never" | "on" | "after">("never"); - // const [endDate, setEndDate] = useState(); - // const [occurrences, setOccurrences] = useState(1); - - const weekDays = [ - { label: "Su", value: 0 }, - { label: "Mo", value: 1 }, - { label: "Tu", value: 2 }, - { label: "We", value: 3 }, - { label: "Th", value: 4 }, - { label: "Fr", value: 5 }, - { label: "Sa", value: 6 }, - ]; - - const months = [ - { label: "Jan", value: "January" }, - { label: "Feb", value: "February" }, - { label: "Mar", value: "March" }, - { label: "Apr", value: "April" }, - { label: "May", value: "May" }, - { label: "Jun", value: "June" }, - { label: "Jul", value: "July" }, - { label: "Aug", value: "August" }, - { label: "Sep", value: "September" }, - { label: "Oct", value: "October" }, - { label: "Nov", value: "November" }, - { label: "Dec", value: "December" }, - ]; - - const cron_manager = new CronExpressionManager(); - - return ( - - - Schedule Task -
-
- - - - - {frequency === "hour" && ( -
- - -
- )} - - {frequency === "custom" && ( -
- - - setCustomInterval({ - ...customInterval, - value: parseInt(e.target.value), - }) - } - /> - -
- )} -
- - {frequency === "weekly" && ( -
-
- - - - -
-
- {weekDays.map((day) => ( - - ))} -
-
- )} - {frequency === "monthly" && ( -
- -
- - - - -
- {showCustomDays && ( -
- {Array.from({ length: 31 }, (_, i) => ( - - ))} -
- )} -
- )} - {frequency === "yearly" && ( -
- -
- -
-
- {months.map((month, index) => ( - - ))} -
-
- )} - - {frequency !== "minute" && frequency !== "hour" && ( -
- - setSelectedTime(e.target.value)} - /> -
- )} - - - {/* - - On the backend, we are using standard cron expressions, - which makes it challenging to add an end date or stop execution - after a certain time using only cron expressions. - (since standard cron expressions have limitations, like the lack of a year field or more...). - - We could also use ranges in cron expression for end dates but It doesm't cover all cases (sometimes break) - - To automatically end the scheduler, we need to store the end date and time occurrence in the database - and modify scheduler.add_job. Currently, we can only stop the scheduler manually from the monitor tab. - - */} - - {/*
- - - setEndType(value) - } - > -
- - -
- -
- - - - - - - - date < new Date()} - fromDate={new Date()} - /> - - -
-
- - - setOccurrences(Number(e.target.value))} - /> - times -
-
-
*/} - -
- - -
-
-
-
- ); -} diff --git a/autogpt_platform/frontend/src/components/edit/control/BlocksControl.tsx b/autogpt_platform/frontend/src/components/edit/control/BlocksControl.tsx index 9de0052b7e..dacc896ec6 100644 --- a/autogpt_platform/frontend/src/components/edit/control/BlocksControl.tsx +++ b/autogpt_platform/frontend/src/components/edit/control/BlocksControl.tsx @@ -55,8 +55,8 @@ export const BlocksControl: React.FC = ({ const [searchQuery, setSearchQuery] = useState(""); const [selectedCategory, setSelectedCategory] = useState(null); - const graphHasWebhookNodes = nodes.some( - (n) => n.data.uiType == BlockUIType.WEBHOOK, + const graphHasWebhookNodes = nodes.some((n) => + [BlockUIType.WEBHOOK, BlockUIType.WEBHOOK_MANUAL].includes(n.data.uiType), ); const graphHasInputNodes = nodes.some( (n) => n.data.uiType == BlockUIType.INPUT, diff --git a/autogpt_platform/frontend/src/components/integrations/api-key-credentials-modal.tsx b/autogpt_platform/frontend/src/components/integrations/api-key-credentials-modal.tsx new file mode 100644 index 0000000000..ace4a32533 --- /dev/null +++ b/autogpt_platform/frontend/src/components/integrations/api-key-credentials-modal.tsx @@ -0,0 +1,163 @@ +import { FC } from "react"; +import { z } from "zod"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import useCredentials from "@/hooks/useCredentials"; +import { + BlockIOCredentialsSubSchema, + CredentialsMetaInput, +} from "@/lib/autogpt-server-api/types"; + +export const APIKeyCredentialsModal: FC<{ + schema: BlockIOCredentialsSubSchema; + open: boolean; + onClose: () => void; + onCredentialsCreate: (creds: CredentialsMetaInput) => void; + siblingInputs?: Record; +}> = ({ schema, open, onClose, onCredentialsCreate, siblingInputs }) => { + const credentials = useCredentials(schema, siblingInputs); + + const formSchema = z.object({ + apiKey: z.string().min(1, "API Key is required"), + title: z.string().min(1, "Name is required"), + expiresAt: z.string().optional(), + }); + + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + apiKey: "", + title: "", + expiresAt: "", + }, + }); + + if (!credentials || credentials.isLoading || !credentials.supportsApiKey) { + return null; + } + + const { provider, providerName, createAPIKeyCredentials } = credentials; + + async function onSubmit(values: z.infer) { + const expiresAt = values.expiresAt + ? new Date(values.expiresAt).getTime() / 1000 + : undefined; + const newCredentials = await createAPIKeyCredentials({ + api_key: values.apiKey, + title: values.title, + expires_at: expiresAt, + }); + onCredentialsCreate({ + provider, + id: newCredentials.id, + type: "api_key", + title: newCredentials.title, + }); + } + + return ( + { + if (!open) onClose(); + }} + > + + + Add new API key for {providerName} + {schema.description && ( + {schema.description} + )} + + +
+ + ( + + API Key + {schema.credentials_scopes && ( + + Required scope(s) for this block:{" "} + {schema.credentials_scopes?.map((s, i, a) => ( + + {s} + {i < a.length - 1 && ", "} + + ))} + + )} + + + + + + )} + /> + ( + + Name + + + + + + )} + /> + ( + + Expiration Date (Optional) + + + + + + )} + /> + + + +
+
+ ); +}; diff --git a/autogpt_platform/frontend/src/components/integrations/credentials-input.tsx b/autogpt_platform/frontend/src/components/integrations/credentials-input.tsx index 690f58de33..293df40603 100644 --- a/autogpt_platform/frontend/src/components/integrations/credentials-input.tsx +++ b/autogpt_platform/frontend/src/components/integrations/credentials-input.tsx @@ -1,12 +1,8 @@ import { FC, useEffect, useMemo, useState } from "react"; -import { z } from "zod"; import { cn } from "@/lib/utils"; -import { useForm } from "react-hook-form"; -import { Input } from "@/components/ui/input"; import { Button } from "@/components/ui/button"; import SchemaTooltip from "@/components/SchemaTooltip"; import useCredentials from "@/hooks/useCredentials"; -import { zodResolver } from "@hookform/resolvers/zod"; import { NotionLogoIcon } from "@radix-ui/react-icons"; import { FaDiscord, @@ -23,22 +19,6 @@ import { CredentialsProviderName, } from "@/lib/autogpt-server-api/types"; import { IconKey, IconKeyPlus, IconUserPlus } from "@/components/ui/icons"; -import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, -} from "@/components/ui/dialog"; -import { - Form, - FormControl, - FormDescription, - FormField, - FormItem, - FormLabel, - FormMessage, -} from "@/components/ui/form"; import { Select, SelectContent, @@ -48,6 +28,11 @@ import { SelectValue, } from "@/components/ui/select"; import { useBackendAPI } from "@/lib/autogpt-server-api/context"; +import { APIKeyCredentialsModal } from "./api-key-credentials-modal"; +import { UserPasswordCredentialsModal } from "./user-password-credentials-modal"; +import { HostScopedCredentialsModal } from "./host-scoped-credentials-modal"; +import { OAuth2FlowWaitingModal } from "./oauth2-flow-waiting-modal"; +import { getHostFromUrl } from "@/lib/utils/url"; const fallbackIcon = FaKey; @@ -63,6 +48,7 @@ export const providerIcons: Record< github: FaGithub, google: FaGoogle, groq: fallbackIcon, + http: fallbackIcon, notion: NotionLogoIcon, nvidia: fallbackIcon, discord: FaDiscord, @@ -114,12 +100,14 @@ export const CredentialsInput: FC<{ selectedCredentials?: CredentialsMetaInput; onSelectCredentials: (newValue?: CredentialsMetaInput) => void; siblingInputs?: Record; + hideIfSingleCredentialAvailable?: boolean; }> = ({ schema, className, selectedCredentials, onSelectCredentials, siblingInputs, + hideIfSingleCredentialAvailable = true, }) => { const [isAPICredentialsModalOpen, setAPICredentialsModalOpen] = useState(false); @@ -127,6 +115,8 @@ export const CredentialsInput: FC<{ isUserPasswordCredentialsModalOpen, setUserPasswordCredentialsModalOpen, ] = useState(false); + const [isHostScopedCredentialsModalOpen, setHostScopedCredentialsModalOpen] = + useState(false); const [isOAuth2FlowInProgress, setOAuth2FlowInProgress] = useState(false); const [oAuthPopupController, setOAuthPopupController] = useState(null); @@ -146,13 +136,27 @@ export const CredentialsInput: FC<{ } }, [credentials, selectedCredentials, onSelectCredentials]); - const singleCredential = useMemo(() => { - if (!credentials || !("savedCredentials" in credentials)) return null; + const { hasRelevantCredentials, singleCredential } = useMemo(() => { + if (!credentials || !("savedCredentials" in credentials)) { + return { + hasRelevantCredentials: false, + singleCredential: null, + }; + } - if (credentials.savedCredentials.length === 1) - return credentials.savedCredentials[0]; + // Simple logic: if we have any saved credentials, we have relevant credentials + const hasRelevant = credentials.savedCredentials.length > 0; - return null; + // Auto-select single credential if only one exists + const single = + credentials.savedCredentials.length === 1 + ? credentials.savedCredentials[0] + : null; + + return { + hasRelevantCredentials: hasRelevant, + singleCredential: single, + }; }, [credentials]); // If only 1 credential is available, auto-select it and hide this input @@ -162,7 +166,11 @@ export const CredentialsInput: FC<{ } }, [singleCredential, selectedCredentials, onSelectCredentials]); - if (!credentials || credentials.isLoading || singleCredential) { + if ( + !credentials || + credentials.isLoading || + (singleCredential && hideIfSingleCredentialAvailable) + ) { return null; } @@ -172,6 +180,7 @@ export const CredentialsInput: FC<{ supportsApiKey, supportsOAuth2, supportsUserPassword, + supportsHostScoped, savedCredentials, oAuthCallback, } = credentials; @@ -265,7 +274,7 @@ export const CredentialsInput: FC<{ ); } - const ProviderIcon = providerIcons[provider]; + const ProviderIcon = providerIcons[provider] || fallbackIcon; const modals = ( <> {supportsApiKey && ( @@ -299,6 +308,18 @@ export const CredentialsInput: FC<{ siblingInputs={siblingInputs} /> )} + {supportsHostScoped && ( + setHostScopedCredentialsModalOpen(false)} + onCredentialsCreate={(creds) => { + onSelectCredentials(creds); + setHostScopedCredentialsModalOpen(false); + }} + siblingInputs={siblingInputs} + /> + )} ); @@ -311,8 +332,8 @@ export const CredentialsInput: FC<{
); - // No saved credentials yet - if (savedCredentials.length === 0) { + // Show credentials creation UI when no relevant credentials exist + if (!hasRelevantCredentials) { return (
{fieldHeader} @@ -336,6 +357,12 @@ export const CredentialsInput: FC<{ Enter username and password )} + {supportsHostScoped && credentials.discriminatorValue && ( + + )}
{modals} {oAuthError && ( @@ -352,6 +379,12 @@ export const CredentialsInput: FC<{ } else if (newValue === "add-api-key") { // Open API key dialog setAPICredentialsModalOpen(true); + } else if (newValue === "add-user-password") { + // Open user password dialog + setUserPasswordCredentialsModalOpen(true); + } else if (newValue === "add-host-scoped") { + // Open host-scoped credentials dialog + setHostScopedCredentialsModalOpen(true); } else { const selectedCreds = savedCredentials.find((c) => c.id == newValue)!; @@ -400,6 +433,15 @@ export const CredentialsInput: FC<{ {credentials.title} ))} + {savedCredentials + .filter((c) => c.type == "host_scoped") + .map((credentials, index) => ( + + + + {credentials.title} + + ))} {supportsOAuth2 && ( @@ -419,6 +461,12 @@ export const CredentialsInput: FC<{ Add new user password )} + {supportsHostScoped && ( + + + Add host-scoped headers + + )} {modals} @@ -428,291 +476,3 @@ export const CredentialsInput: FC<{ ); }; - -export const APIKeyCredentialsModal: FC<{ - schema: BlockIOCredentialsSubSchema; - open: boolean; - onClose: () => void; - onCredentialsCreate: (creds: CredentialsMetaInput) => void; - siblingInputs?: Record; -}> = ({ schema, open, onClose, onCredentialsCreate, siblingInputs }) => { - const credentials = useCredentials(schema, siblingInputs); - - const formSchema = z.object({ - apiKey: z.string().min(1, "API Key is required"), - title: z.string().min(1, "Name is required"), - expiresAt: z.string().optional(), - }); - - const form = useForm>({ - resolver: zodResolver(formSchema), - defaultValues: { - apiKey: "", - title: "", - expiresAt: "", - }, - }); - - if (!credentials || credentials.isLoading || !credentials.supportsApiKey) { - return null; - } - - const { provider, providerName, createAPIKeyCredentials } = credentials; - - async function onSubmit(values: z.infer) { - const expiresAt = values.expiresAt - ? new Date(values.expiresAt).getTime() / 1000 - : undefined; - const newCredentials = await createAPIKeyCredentials({ - api_key: values.apiKey, - title: values.title, - expires_at: expiresAt, - }); - onCredentialsCreate({ - provider, - id: newCredentials.id, - type: "api_key", - title: newCredentials.title, - }); - } - - return ( - { - if (!open) onClose(); - }} - > - - - Add new API key for {providerName} - {schema.description && ( - {schema.description} - )} - - -
- - ( - - API Key - {schema.credentials_scopes && ( - - Required scope(s) for this block:{" "} - {schema.credentials_scopes?.map((s, i, a) => ( - - {s} - {i < a.length - 1 && ", "} - - ))} - - )} - - - - - - )} - /> - ( - - Name - - - - - - )} - /> - ( - - Expiration Date (Optional) - - - - - - )} - /> - - - -
-
- ); -}; - -export const UserPasswordCredentialsModal: FC<{ - schema: BlockIOCredentialsSubSchema; - open: boolean; - onClose: () => void; - onCredentialsCreate: (creds: CredentialsMetaInput) => void; - siblingInputs?: Record; -}> = ({ schema, open, onClose, onCredentialsCreate, siblingInputs }) => { - const credentials = useCredentials(schema, siblingInputs); - - const formSchema = z.object({ - username: z.string().min(1, "Username is required"), - password: z.string().min(1, "Password is required"), - title: z.string().min(1, "Name is required"), - }); - - const form = useForm>({ - resolver: zodResolver(formSchema), - defaultValues: { - username: "", - password: "", - title: "", - }, - }); - - if ( - !credentials || - credentials.isLoading || - !credentials.supportsUserPassword - ) { - return null; - } - - const { provider, providerName, createUserPasswordCredentials } = credentials; - - async function onSubmit(values: z.infer) { - const newCredentials = await createUserPasswordCredentials({ - username: values.username, - password: values.password, - title: values.title, - }); - onCredentialsCreate({ - provider, - id: newCredentials.id, - type: "user_password", - title: newCredentials.title, - }); - } - - return ( - { - if (!open) onClose(); - }} - > - - - - Add new username & password for {providerName} - - -
- - ( - - Username - - - - - - )} - /> - ( - - Password - - - - - - )} - /> - ( - - Name - - - - - - )} - /> - - - -
-
- ); -}; - -export const OAuth2FlowWaitingModal: FC<{ - open: boolean; - onClose: () => void; - providerName: string; -}> = ({ open, onClose, providerName }) => { - return ( - { - if (!open) onClose(); - }} - > - - - - Waiting on {providerName} sign-in process... - - - Complete the sign-in process in the pop-up window. -
- Closing this dialog will cancel the sign-in process. -
-
-
-
- ); -}; diff --git a/autogpt_platform/frontend/src/components/integrations/credentials-provider.tsx b/autogpt_platform/frontend/src/components/integrations/credentials-provider.tsx index 26823d0177..09072c4d1a 100644 --- a/autogpt_platform/frontend/src/components/integrations/credentials-provider.tsx +++ b/autogpt_platform/frontend/src/components/integrations/credentials-provider.tsx @@ -6,10 +6,12 @@ import { CredentialsDeleteResponse, CredentialsMetaResponse, CredentialsProviderName, + HostScopedCredentials, PROVIDER_NAMES, UserPasswordCredentials, } from "@/lib/autogpt-server-api"; import { useBackendAPI } from "@/lib/autogpt-server-api/context"; +import { useToastOnFail } from "@/components/ui/use-toast"; // Get keys from CredentialsProviderName type const CREDENTIALS_PROVIDER_NAMES = Object.values( @@ -30,6 +32,7 @@ const providerDisplayNames: Record = { google: "Google", google_maps: "Google Maps", groq: "Groq", + http: "HTTP", hubspot: "Hubspot", ideogram: "Ideogram", jina: "Jina", @@ -68,6 +71,11 @@ type UserPasswordCredentialsCreatable = Omit< "id" | "provider" | "type" >; +type HostScopedCredentialsCreatable = Omit< + HostScopedCredentials, + "id" | "provider" | "type" +>; + export type CredentialsProviderData = { provider: CredentialsProviderName; providerName: string; @@ -82,6 +90,9 @@ export type CredentialsProviderData = { createUserPasswordCredentials: ( credentials: UserPasswordCredentialsCreatable, ) => Promise; + createHostScopedCredentials: ( + credentials: HostScopedCredentialsCreatable, + ) => Promise; deleteCredentials: ( id: string, force?: boolean, @@ -106,6 +117,7 @@ export default function CredentialsProvider({ useState(null); const { isLoggedIn } = useSupabase(); const api = useBackendAPI(); + const onFailToast = useToastOnFail(); const addCredentials = useCallback( ( @@ -134,11 +146,16 @@ export default function CredentialsProvider({ code: string, state_token: string, ): Promise => { - const credsMeta = await api.oAuthCallback(provider, code, state_token); - addCredentials(provider, credsMeta); - return credsMeta; + try { + const credsMeta = await api.oAuthCallback(provider, code, state_token); + addCredentials(provider, credsMeta); + return credsMeta; + } catch (error) { + onFailToast("complete OAuth authentication")(error); + throw error; + } }, - [api, addCredentials], + [api, addCredentials, onFailToast], ); /** Wraps `BackendAPI.createAPIKeyCredentials`, and adds the result to the internal credentials store. */ @@ -147,14 +164,19 @@ export default function CredentialsProvider({ provider: CredentialsProviderName, credentials: APIKeyCredentialsCreatable, ): Promise => { - const credsMeta = await api.createAPIKeyCredentials({ - provider, - ...credentials, - }); - addCredentials(provider, credsMeta); - return credsMeta; + try { + const credsMeta = await api.createAPIKeyCredentials({ + provider, + ...credentials, + }); + addCredentials(provider, credsMeta); + return credsMeta; + } catch (error) { + onFailToast("create API key credentials")(error); + throw error; + } }, - [api, addCredentials], + [api, addCredentials, onFailToast], ); /** Wraps `BackendAPI.createUserPasswordCredentials`, and adds the result to the internal credentials store. */ @@ -163,14 +185,40 @@ export default function CredentialsProvider({ provider: CredentialsProviderName, credentials: UserPasswordCredentialsCreatable, ): Promise => { - const credsMeta = await api.createUserPasswordCredentials({ - provider, - ...credentials, - }); - addCredentials(provider, credsMeta); - return credsMeta; + try { + const credsMeta = await api.createUserPasswordCredentials({ + provider, + ...credentials, + }); + addCredentials(provider, credsMeta); + return credsMeta; + } catch (error) { + onFailToast("create user/password credentials")(error); + throw error; + } }, - [api, addCredentials], + [api, addCredentials, onFailToast], + ); + + /** Wraps `BackendAPI.createHostScopedCredentials`, and adds the result to the internal credentials store. */ + const createHostScopedCredentials = useCallback( + async ( + provider: CredentialsProviderName, + credentials: HostScopedCredentialsCreatable, + ): Promise => { + try { + const credsMeta = await api.createHostScopedCredentials({ + provider, + ...credentials, + }); + addCredentials(provider, credsMeta); + return credsMeta; + } catch (error) { + onFailToast("create host-scoped credentials")(error); + throw error; + } + }, + [api, addCredentials, onFailToast], ); /** Wraps `BackendAPI.deleteCredentials`, and removes the credentials from the internal store. */ @@ -182,26 +230,31 @@ export default function CredentialsProvider({ ): Promise< CredentialsDeleteResponse | CredentialsDeleteNeedConfirmationResponse > => { - const result = await api.deleteCredentials(provider, id, force); - if (!result.deleted) { - return result; - } - setProviders((prev) => { - if (!prev || !prev[provider]) return prev; + try { + const result = await api.deleteCredentials(provider, id, force); + if (!result.deleted) { + return result; + } + setProviders((prev) => { + if (!prev || !prev[provider]) return prev; - return { - ...prev, - [provider]: { - ...prev[provider], - savedCredentials: prev[provider].savedCredentials.filter( - (cred) => cred.id !== id, - ), - }, - }; - }); - return result; + return { + ...prev, + [provider]: { + ...prev[provider], + savedCredentials: prev[provider].savedCredentials.filter( + (cred) => cred.id !== id, + ), + }, + }; + }); + return result; + } catch (error) { + onFailToast("delete credentials")(error); + throw error; + } }, - [api], + [api, onFailToast], ); useEffect(() => { @@ -210,47 +263,54 @@ export default function CredentialsProvider({ return; } - api.listCredentials().then((response) => { - const credentialsByProvider = response.reduce( - (acc, cred) => { - if (!acc[cred.provider]) { - acc[cred.provider] = []; - } - acc[cred.provider].push(cred); - return acc; - }, - {} as Record, - ); + api + .listCredentials() + .then((response) => { + const credentialsByProvider = response.reduce( + (acc, cred) => { + if (!acc[cred.provider]) { + acc[cred.provider] = []; + } + acc[cred.provider].push(cred); + return acc; + }, + {} as Record, + ); - setProviders((prev) => ({ - ...prev, - ...Object.fromEntries( - CREDENTIALS_PROVIDER_NAMES.map((provider) => [ - provider, - { + setProviders((prev) => ({ + ...prev, + ...Object.fromEntries( + CREDENTIALS_PROVIDER_NAMES.map((provider) => [ provider, - providerName: providerDisplayNames[provider], - savedCredentials: credentialsByProvider[provider] ?? [], - oAuthCallback: (code: string, state_token: string) => - oAuthCallback(provider, code, state_token), - createAPIKeyCredentials: ( - credentials: APIKeyCredentialsCreatable, - ) => createAPIKeyCredentials(provider, credentials), - createUserPasswordCredentials: ( - credentials: UserPasswordCredentialsCreatable, - ) => createUserPasswordCredentials(provider, credentials), - deleteCredentials: (id: string, force: boolean = false) => - deleteCredentials(provider, id, force), - } satisfies CredentialsProviderData, - ]), - ), - })); - }); + { + provider, + providerName: providerDisplayNames[provider], + savedCredentials: credentialsByProvider[provider] ?? [], + oAuthCallback: (code: string, state_token: string) => + oAuthCallback(provider, code, state_token), + createAPIKeyCredentials: ( + credentials: APIKeyCredentialsCreatable, + ) => createAPIKeyCredentials(provider, credentials), + createUserPasswordCredentials: ( + credentials: UserPasswordCredentialsCreatable, + ) => createUserPasswordCredentials(provider, credentials), + createHostScopedCredentials: ( + credentials: HostScopedCredentialsCreatable, + ) => createHostScopedCredentials(provider, credentials), + deleteCredentials: (id: string, force: boolean = false) => + deleteCredentials(provider, id, force), + } satisfies CredentialsProviderData, + ]), + ), + })); + }) + .catch(onFailToast("load credentials")); }, [ api, isLoggedIn, createAPIKeyCredentials, createUserPasswordCredentials, + createHostScopedCredentials, deleteCredentials, oAuthCallback, ]); diff --git a/autogpt_platform/frontend/src/components/integrations/host-scoped-credentials-modal.tsx b/autogpt_platform/frontend/src/components/integrations/host-scoped-credentials-modal.tsx new file mode 100644 index 0000000000..ffdf35569d --- /dev/null +++ b/autogpt_platform/frontend/src/components/integrations/host-scoped-credentials-modal.tsx @@ -0,0 +1,235 @@ +import { FC, useEffect, useState } from "react"; +import { z } from "zod"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import useCredentials from "@/hooks/useCredentials"; +import { + BlockIOCredentialsSubSchema, + CredentialsMetaInput, +} from "@/lib/autogpt-server-api/types"; +import { getHostFromUrl } from "@/lib/utils/url"; + +export const HostScopedCredentialsModal: FC<{ + schema: BlockIOCredentialsSubSchema; + open: boolean; + onClose: () => void; + onCredentialsCreate: (creds: CredentialsMetaInput) => void; + siblingInputs?: Record; +}> = ({ schema, open, onClose, onCredentialsCreate, siblingInputs }) => { + const credentials = useCredentials(schema, siblingInputs); + + // Get current host from siblingInputs or discriminator_values + const currentUrl = credentials?.discriminatorValue; + const currentHost = currentUrl ? getHostFromUrl(currentUrl) : ""; + + const formSchema = z.object({ + host: z.string().min(1, "Host is required"), + title: z.string().optional(), + headers: z.record(z.string()).optional(), + }); + + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + host: currentHost || "", + title: currentHost || "Manual Entry", + headers: {}, + }, + }); + + const [headerPairs, setHeaderPairs] = useState< + Array<{ key: string; value: string }> + >([{ key: "", value: "" }]); + + // Update form values when siblingInputs change + useEffect(() => { + if (currentHost) { + form.setValue("host", currentHost); + form.setValue("title", currentHost); + } else { + // Reset to empty when no current host + form.setValue("host", ""); + form.setValue("title", "Manual Entry"); + } + }, [currentHost, form]); + + if ( + !credentials || + credentials.isLoading || + !credentials.supportsHostScoped + ) { + return null; + } + + const { provider, providerName, createHostScopedCredentials } = credentials; + + const addHeaderPair = () => { + setHeaderPairs([...headerPairs, { key: "", value: "" }]); + }; + + const removeHeaderPair = (index: number) => { + if (headerPairs.length > 1) { + setHeaderPairs(headerPairs.filter((_, i) => i !== index)); + } + }; + + const updateHeaderPair = ( + index: number, + field: "key" | "value", + value: string, + ) => { + const newPairs = [...headerPairs]; + newPairs[index][field] = value; + setHeaderPairs(newPairs); + }; + + async function onSubmit(values: z.infer) { + // Convert header pairs to object, filtering out empty pairs + const headers = headerPairs.reduce( + (acc, pair) => { + if (pair.key.trim() && pair.value.trim()) { + acc[pair.key.trim()] = pair.value.trim(); + } + return acc; + }, + {} as Record, + ); + + const newCredentials = await createHostScopedCredentials({ + host: values.host, + title: currentHost || values.host, + headers, + }); + + onCredentialsCreate({ + provider, + id: newCredentials.id, + type: "host_scoped", + title: newCredentials.title, + }); + } + + return ( + { + if (!open) onClose(); + }} + > + + + Add sensitive headers for {providerName} + {schema.description && ( + {schema.description} + )} + + +
+ + ( + + Host Pattern + + {currentHost + ? "Auto-populated from the URL field. Headers will be applied to requests to this host." + : "Enter the host/domain to match against request URLs (e.g., api.example.com)."} + + + + + + + )} + /> + +
+ Headers + + Add sensitive headers (like Authorization, X-API-Key) that + should be automatically included in requests to the specified + host. + + + {headerPairs.map((pair, index) => ( +
+
+ + updateHeaderPair(index, "key", e.target.value) + } + /> +
+
+ + updateHeaderPair(index, "value", e.target.value) + } + /> +
+ +
+ ))} + + +
+ + + + +
+
+ ); +}; diff --git a/autogpt_platform/frontend/src/components/integrations/oauth2-flow-waiting-modal.tsx b/autogpt_platform/frontend/src/components/integrations/oauth2-flow-waiting-modal.tsx new file mode 100644 index 0000000000..98879568ca --- /dev/null +++ b/autogpt_platform/frontend/src/components/integrations/oauth2-flow-waiting-modal.tsx @@ -0,0 +1,36 @@ +import { FC } from "react"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; + +export const OAuth2FlowWaitingModal: FC<{ + open: boolean; + onClose: () => void; + providerName: string; +}> = ({ open, onClose, providerName }) => { + return ( + { + if (!open) onClose(); + }} + > + + + + Waiting on {providerName} sign-in process... + + + Complete the sign-in process in the pop-up window. +
+ Closing this dialog will cancel the sign-in process. +
+
+
+
+ ); +}; diff --git a/autogpt_platform/frontend/src/components/integrations/user-password-credentials-modal.tsx b/autogpt_platform/frontend/src/components/integrations/user-password-credentials-modal.tsx new file mode 100644 index 0000000000..ad16077d72 --- /dev/null +++ b/autogpt_platform/frontend/src/components/integrations/user-password-credentials-modal.tsx @@ -0,0 +1,149 @@ +import { FC } from "react"; +import { z } from "zod"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import { + Dialog, + DialogContent, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { + Form, + FormControl, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import useCredentials from "@/hooks/useCredentials"; +import { + BlockIOCredentialsSubSchema, + CredentialsMetaInput, +} from "@/lib/autogpt-server-api/types"; + +export const UserPasswordCredentialsModal: FC<{ + schema: BlockIOCredentialsSubSchema; + open: boolean; + onClose: () => void; + onCredentialsCreate: (creds: CredentialsMetaInput) => void; + siblingInputs?: Record; +}> = ({ schema, open, onClose, onCredentialsCreate, siblingInputs }) => { + const credentials = useCredentials(schema, siblingInputs); + + const formSchema = z.object({ + username: z.string().min(1, "Username is required"), + password: z.string().min(1, "Password is required"), + title: z.string().min(1, "Name is required"), + }); + + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + username: "", + password: "", + title: "", + }, + }); + + if ( + !credentials || + credentials.isLoading || + !credentials.supportsUserPassword + ) { + return null; + } + + const { provider, providerName, createUserPasswordCredentials } = credentials; + + async function onSubmit(values: z.infer) { + const newCredentials = await createUserPasswordCredentials({ + username: values.username, + password: values.password, + title: values.title, + }); + onCredentialsCreate({ + provider, + id: newCredentials.id, + type: "user_password", + title: newCredentials.title, + }); + } + + return ( + { + if (!open) onClose(); + }} + > + + + + Add new username & password for {providerName} + + +
+ + ( + + Username + + + + + + )} + /> + ( + + Password + + + + + + )} + /> + ( + + Name + + + + + + )} + /> + + + +
+
+ ); +}; diff --git a/autogpt_platform/frontend/src/components/library/library-agent-list.tsx b/autogpt_platform/frontend/src/components/library/library-agent-list.tsx deleted file mode 100644 index d98b4077f8..0000000000 --- a/autogpt_platform/frontend/src/components/library/library-agent-list.tsx +++ /dev/null @@ -1,95 +0,0 @@ -"use client"; -import { useEffect, useState, useCallback } from "react"; - -import { useBackendAPI } from "@/lib/autogpt-server-api/context"; - -import { useLibraryPageContext } from "@/app/(platform)/library/state-provider"; -import { useScrollThreshold } from "@/hooks/useScrollThreshold"; -import LibraryAgentCard from "./library-agent-card"; - -/** - * Displays a grid of library agents with infinite scroll functionality. - */ -export default function LibraryAgentList(): React.ReactNode { - const [currentPage, setCurrentPage] = useState(1); - const [loadingMore, setLoadingMore] = useState(false); - const [hasMore, setHasMore] = useState(true); - - const api = useBackendAPI(); - const { agents, setAgents, setAgentLoading, agentLoading } = - useLibraryPageContext(); - - const fetchAgents = useCallback( - async (page: number) => { - try { - const response = await api.listLibraryAgents( - page === 1 ? {} : { page: page }, - ); - if (page > 1) { - setAgents((prevAgent) => [...prevAgent, ...response.agents]); - } else { - setAgents(response.agents); - } - setHasMore( - response.pagination.current_page * response.pagination.page_size < - response.pagination.total_items, - ); - } finally { - setAgentLoading(false); - setLoadingMore(false); - } - }, - [api, setAgents, setAgentLoading], - ); - - useEffect(() => { - fetchAgents(1); - }, [fetchAgents]); - - const handleInfiniteScroll = useCallback( - (scrollY: number) => { - if (!hasMore || loadingMore) return; - - const { scrollHeight, clientHeight } = document.documentElement; - const SCROLL_THRESHOLD = 20; - const FETCH_DELAY = 1000; - - if (scrollY + clientHeight >= scrollHeight - SCROLL_THRESHOLD) { - setLoadingMore(true); - const nextPage = currentPage + 1; - setCurrentPage(nextPage); - setTimeout(() => fetchAgents(nextPage), FETCH_DELAY); - } - }, - [currentPage, hasMore, loadingMore, fetchAgents], - ); - - useScrollThreshold(handleInfiniteScroll, 50); - - const LoadingSpinner = () => ( -
- ); - - return ( -
- {agentLoading ? ( -
- -
- ) : ( - <> -
- {agents.map((agent) => ( - - ))} -
- {loadingMore && hasMore && ( -
- -
- )} - - )} -
- ); -} diff --git a/autogpt_platform/frontend/src/components/library/library-search-bar.tsx b/autogpt_platform/frontend/src/components/library/library-search-bar.tsx deleted file mode 100644 index a33bd63c9f..0000000000 --- a/autogpt_platform/frontend/src/components/library/library-search-bar.tsx +++ /dev/null @@ -1,74 +0,0 @@ -"use client"; -import { useRef, useState } from "react"; -import debounce from "lodash/debounce"; -import { Input } from "@/components/ui/input"; -import { Search, X } from "lucide-react"; -import { useBackendAPI } from "@/lib/autogpt-server-api/context"; -import { useLibraryPageContext } from "@/app/(platform)/library/state-provider"; - -export default function LibrarySearchBar(): React.ReactNode { - const inputRef = useRef(null); - const [isFocused, setIsFocused] = useState(false); - const api = useBackendAPI(); - const { setAgentLoading, setAgents, librarySort, setSearchTerm } = - useLibraryPageContext(); - - const debouncedSearch = debounce(async (value: string) => { - try { - setAgentLoading(true); - setSearchTerm(value); - await new Promise((resolve) => setTimeout(resolve, 1000)); - const response = await api.listLibraryAgents({ - search_term: value, - sort_by: librarySort, - page: 1, - }); - setAgents(response.agents); - setAgentLoading(false); - } catch (error) { - console.error("Search failed:", error); - } - }, 300); - const handleSearchInput = (e: React.ChangeEvent) => { - const searchTerm = e.target.value; - debouncedSearch(searchTerm); - }; - - return ( -
inputRef.current?.focus()} - className="relative z-[21] mx-auto flex h-[50px] w-full max-w-[500px] flex-1 cursor-pointer items-center rounded-[45px] bg-[#EDEDED] px-[24px] py-[10px]" - > - - - setIsFocused(true)} - onBlur={() => !inputRef.current?.value && setIsFocused(false)} - onChange={handleSearchInput} - className="flex-1 border-none font-sans text-[16px] font-normal leading-7 shadow-none focus:shadow-none focus:ring-0" - type="text" - placeholder="Search agents" - /> - - {isFocused && inputRef.current?.value && ( - { - if (inputRef.current) { - debouncedSearch(""); - inputRef.current.value = ""; - inputRef.current.blur(); - e.preventDefault(); - } - setIsFocused(false); - }} - /> - )} -
- ); -} diff --git a/autogpt_platform/frontend/src/components/molecules/Dialog/Dialog.stories.tsx b/autogpt_platform/frontend/src/components/molecules/Dialog/Dialog.stories.tsx new file mode 100644 index 0000000000..9a57d0ab0e --- /dev/null +++ b/autogpt_platform/frontend/src/components/molecules/Dialog/Dialog.stories.tsx @@ -0,0 +1,197 @@ +import { Button } from "@/components/atoms/Button/Button"; +import type { Meta, StoryObj } from "@storybook/nextjs"; +import { useState } from "react"; +import { Dialog } from "./Dialog"; + +const meta: Meta = { + title: "Molecules/Dialog", + component: Dialog, + parameters: { + layout: "centered", + docs: { + description: { + component: + "A responsive dialog component that automatically switches between modal dialog (desktop) and drawer (mobile). Built on top of Radix UI Dialog and Vaul drawer with custom styling. Supports compound components: Dialog.Trigger, Dialog.Content, and Dialog.Footer.", + }, + }, + }, + argTypes: { + title: { + control: "text", + description: "Dialog title - can be string or React node", + }, + forceOpen: { + control: "boolean", + description: "Force the dialog to stay open (useful for previewing)", + }, + styling: { + control: "object", + description: "Custom CSS styles object", + }, + onClose: { + action: "closed", + description: "Callback fired when dialog closes", + }, + }, + args: { + title: "Dialog Title", + }, +}; + +export default meta; +type Story = StoryObj; + +export const Basic: Story = { + render: renderBasicDialog, +}; + +export const WithoutTitle: Story = { + render: renderDialogWithoutTitle, +}; + +export const ForceOpen: Story = { + args: { + forceOpen: true, + title: "Preview Dialog", + }, + render: renderForceOpenDialog, +}; + +export const WithFooter: Story = { + render: renderDialogWithFooter, +}; + +export const Controlled: Story = { + render: renderControlledDialog, +}; + +export const CustomStyling: Story = { + render: renderCustomStyledDialog, +}; + +function renderBasicDialog() { + return ( + + + + + +

This is a basic dialog with some content.

+

+ It automatically adapts to screen size - modal on desktop, drawer on + mobile. +

+
+
+ ); +} + +function renderDialogWithoutTitle() { + return ( + + + + + +

+ This dialog doesn't use the title prop, allowing for no header or + a custom header. +

+
+
+ ); +} + +function renderForceOpenDialog(args: any) { + return ( + + +

This dialog is forced open for preview purposes.

+

+ In real usage, you'd typically use a trigger or controlled state. +

+
+
+ ); +} + +function renderDialogWithFooter() { + return ( + + + + + +

This dialog includes a footer with action buttons.

+

Use the footer for primary and secondary actions.

+ + + + +
+
+ ); +} + +function renderControlledDialog() { + const [isOpen, setIsOpen] = useState(false); + + const handleToggle = async () => { + setIsOpen(!isOpen); + }; + + return ( +
+ + + console.log("Dialog closed")} + > + +
+

This dialog is controlled by external state.

+

+ Open state:{" "} + {isOpen ? "Open" : "Closed"} +

+
+ +
+
+
+ ); +} + +function renderCustomStyledDialog() { + return ( + + + + + +

This dialog has custom styling applied.

+

You can customize dimensions, colors, and other CSS properties.

+
+
+ ); +} diff --git a/autogpt_platform/frontend/src/components/molecules/Dialog/Dialog.tsx b/autogpt_platform/frontend/src/components/molecules/Dialog/Dialog.tsx new file mode 100644 index 0000000000..7ecb99ede7 --- /dev/null +++ b/autogpt_platform/frontend/src/components/molecules/Dialog/Dialog.tsx @@ -0,0 +1,86 @@ +"use client"; +import * as RXDialog from "@radix-ui/react-dialog"; +import { CSSProperties, PropsWithChildren } from "react"; +import { Drawer } from "vaul"; + +import { BaseContent } from "./components/BaseContent"; +import { BaseFooter } from "./components/BaseFooter"; +import { BaseTrigger } from "./components/BaseTrigger"; +import { DialogCtx, useDialogCtx } from "./useDialogCtx"; +import { useDialogInternal } from "./useDialogInternal"; + +interface Props extends PropsWithChildren { + title?: React.ReactNode; + styling?: CSSProperties; + + forceOpen?: boolean; + onClose?: (() => void) | undefined; + controlled?: { + isOpen: boolean; + set: (open: boolean) => Promise | void; + }; +} + +Dialog.Trigger = BaseTrigger; +Dialog.Content = BaseContent; +Dialog.Footer = BaseFooter; + +function Dialog({ + children, + title, + styling, + + forceOpen = false, + onClose, + controlled, +}: Props) { + const config = useDialogInternal({ controlled }); + const isOpen = forceOpen || config.isOpen; + + return ( + { + await config.handleClose(); + onClose?.(); + }, + }} + > + {config.isLgScreenUp ? ( + { + if (!open) { + config.handleClose(); + onClose?.(); + } + }} + > + {children} + + ) : ( + { + if (!open) { + config.handleClose(); + onClose?.(); + } + }} + > + {children} + + )} + + ); +} + +export { Dialog, useDialogCtx }; diff --git a/autogpt_platform/frontend/src/components/molecules/Dialog/components/BaseContent.tsx b/autogpt_platform/frontend/src/components/molecules/Dialog/components/BaseContent.tsx new file mode 100644 index 0000000000..9ad2e3bab5 --- /dev/null +++ b/autogpt_platform/frontend/src/components/molecules/Dialog/components/BaseContent.tsx @@ -0,0 +1,17 @@ +import { PropsWithChildren } from "react"; + +import { useDialogCtx } from "../useDialogCtx"; +import { DialogWrap } from "./DialogWrap"; +import { DrawerWrap } from "./DrawerWrap"; + +type Props = PropsWithChildren; + +export function BaseContent({ children }: Props) { + const ctx = useDialogCtx(); + + return ctx.isLargeScreen ? ( + {children} + ) : ( + {children} + ); +} diff --git a/autogpt_platform/frontend/src/components/molecules/Dialog/components/BaseFooter.tsx b/autogpt_platform/frontend/src/components/molecules/Dialog/components/BaseFooter.tsx new file mode 100644 index 0000000000..ce730569c8 --- /dev/null +++ b/autogpt_platform/frontend/src/components/molecules/Dialog/components/BaseFooter.tsx @@ -0,0 +1,31 @@ +import { useDialogCtx } from "../useDialogCtx"; + +interface Props { + children: React.ReactNode; + testId?: string; + className?: string; +} + +export function BaseFooter({ + children, + testId = "modal-footer", + className = "", +}: Props) { + const ctx = useDialogCtx(); + + return ctx.isLargeScreen ? ( +
+ {children} +
+ ) : ( +
+ {children} +
+ ); +} diff --git a/autogpt_platform/frontend/src/components/molecules/Dialog/components/BaseTrigger.tsx b/autogpt_platform/frontend/src/components/molecules/Dialog/components/BaseTrigger.tsx new file mode 100644 index 0000000000..36db5305c4 --- /dev/null +++ b/autogpt_platform/frontend/src/components/molecules/Dialog/components/BaseTrigger.tsx @@ -0,0 +1,12 @@ +import React, { PropsWithChildren } from "react"; + +import { useDialogCtx } from "../useDialogCtx"; + +export function BaseTrigger({ children }: PropsWithChildren) { + const ctx = useDialogCtx(); + + return React.cloneElement(children as React.ReactElement, { + onClick: ctx.handleOpen, + className: "cursor-pointer", + }); +} diff --git a/autogpt_platform/frontend/src/components/molecules/Dialog/components/DialogWrap.tsx b/autogpt_platform/frontend/src/components/molecules/Dialog/components/DialogWrap.tsx new file mode 100644 index 0000000000..2aec6333a5 --- /dev/null +++ b/autogpt_platform/frontend/src/components/molecules/Dialog/components/DialogWrap.tsx @@ -0,0 +1,66 @@ +import { cn } from "@/lib/utils"; +import { X } from "@phosphor-icons/react"; +import * as RXDialog from "@radix-ui/react-dialog"; +import { CSSProperties, PropsWithChildren } from "react"; +import { DialogCtx } from "../useDialogCtx"; +import { modalStyles } from "./styles"; + +type BaseProps = DialogCtx & PropsWithChildren; + +interface Props extends BaseProps { + title: React.ReactNode; + styling: CSSProperties | undefined; + withGradient?: boolean; +} + +export function DialogWrap({ + children, + title, + styling = {}, + isForceOpen, + handleClose, +}: Props) { + return ( + + + +
+ {title ? ( + + {title} + + ) : ( + + {/* Title is required for a11y compliance even if not displayed so screen readers can announce it */} + {title} + + )} + + {isForceOpen && !handleClose ? null : ( + + )} +
+
{children}
+
+
+ ); +} diff --git a/autogpt_platform/frontend/src/components/molecules/Dialog/components/DrawerWrap.tsx b/autogpt_platform/frontend/src/components/molecules/Dialog/components/DrawerWrap.tsx new file mode 100644 index 0000000000..11d33826b7 --- /dev/null +++ b/autogpt_platform/frontend/src/components/molecules/Dialog/components/DrawerWrap.tsx @@ -0,0 +1,63 @@ +import { Button } from "@/components/ui/button"; +import { X } from "@phosphor-icons/react"; +import { PropsWithChildren } from "react"; +import { Drawer } from "vaul"; +import { DialogCtx } from "../useDialogCtx"; +import { drawerStyles, modalStyles } from "./styles"; + +type BaseProps = DialogCtx & PropsWithChildren; + +interface Props extends BaseProps { + testId?: string; + title: React.ReactNode; + handleClose: () => void; +} + +export function DrawerWrap({ + children, + title, + testId, + handleClose, + isForceOpen, +}: Props) { + const closeBtn = ( + + ); + + return ( + + + +
+ {title ? ( + {title} + ) : null} + + {!isForceOpen ? ( + title ? ( + closeBtn + ) : ( +
+ {closeBtn} +
+ ) + ) : null} +
+
{children}
+
+
+ ); +} diff --git a/autogpt_platform/frontend/src/components/molecules/Dialog/components/styles.ts b/autogpt_platform/frontend/src/components/molecules/Dialog/components/styles.ts new file mode 100644 index 0000000000..d430d703af --- /dev/null +++ b/autogpt_platform/frontend/src/components/molecules/Dialog/components/styles.ts @@ -0,0 +1,23 @@ +// Common styles as Tailwind class strings +const commonStyles = { + title: "font-poppins text-md md:text-lg leading-none", + overlay: + "fixed inset-0 z-50 bg-stone-500/20 dark:bg-black/50 backdrop-blur-md animate-fade-in", + content: + "bg-stone-100 dark:bg-stone-800 p-6 fixed rounded-xl flex flex-col z-50 w-full overflow-hidden", +}; + +// Modal specific styles +export const modalStyles = { + ...commonStyles, + content: `${commonStyles.content} p-6 border border-stone-200 dark:border-stone-700 overflow-y-auto min-w-[40vw] max-w-[60vw] max-h-[95vh] top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 animate-fadein`, + iconWrap: + "absolute top-2 right-3 bg-transparent p-2 rounded-full transition-colors duration-300 ease-in-out outline-none border-none", + icon: "w-4 h-4 text-stone-800 dark:text-stone-300", +}; + +// Drawer specific styles +export const drawerStyles = { + ...commonStyles, + content: `${commonStyles.content} max-h-[90vh] w-full bottom-0 rounded-br-none rounded-bl-none`, +}; diff --git a/autogpt_platform/frontend/src/components/molecules/Dialog/useDialogCtx.ts b/autogpt_platform/frontend/src/components/molecules/Dialog/useDialogCtx.ts new file mode 100644 index 0000000000..6d0051d7a4 --- /dev/null +++ b/autogpt_platform/frontend/src/components/molecules/Dialog/useDialogCtx.ts @@ -0,0 +1,27 @@ +import { CSSProperties, createContext, useContext } from "react"; + +export function useDialogCtx() { + const modalContext = useContext(DialogCtx); + + return modalContext; +} + +export interface DialogCtx { + title: React.ReactNode; + handleOpen: () => void; + handleClose: () => void; + isOpen: boolean; + isForceOpen: boolean; + isLargeScreen: boolean; + styling: CSSProperties | undefined; +} + +export const DialogCtx = createContext({ + title: "", + isOpen: false, + isForceOpen: false, + isLargeScreen: true, + handleOpen: () => undefined, + handleClose: () => undefined, + styling: {}, +}); diff --git a/autogpt_platform/frontend/src/components/molecules/Dialog/useDialogInternal.ts b/autogpt_platform/frontend/src/components/molecules/Dialog/useDialogInternal.ts new file mode 100644 index 0000000000..1973fa9a8e --- /dev/null +++ b/autogpt_platform/frontend/src/components/molecules/Dialog/useDialogInternal.ts @@ -0,0 +1,51 @@ +import { isLargeScreen, useBreakpoint } from "@/lib/hooks/useBreakpoint"; +import { useEffect, useState } from "react"; + +type Args = { + controlled: + | { + isOpen: boolean; + set: (open: boolean) => Promise | void; + } + | undefined; +}; + +export function useDialogInternal({ controlled }: Args) { + const [isOpen, setIsOpen] = useState(false); + + const breakpoint = useBreakpoint(); + + const [isLgScreenUp, setIsLgScreenUp] = useState(isLargeScreen(breakpoint)); + + useEffect(() => { + setIsLgScreenUp(isLargeScreen(breakpoint)); + }, [breakpoint]); + + // if first opened as modal, or drawer - we need to keep it this way + // because, given the current implementation, we can't switch between modal and drawer without a full remount + + async function handleOpen() { + setIsLgScreenUp(isLargeScreen(breakpoint)); + + if (controlled) { + await controlled.set(true); + } else { + setIsOpen(true); + } + } + + async function handleClose() { + if (controlled) { + await controlled.set(false); + } else { + setIsOpen(false); + } + } + + return { + isOpen: controlled ? controlled.isOpen : isOpen, + handleOpen, + handleClose, + isLgScreenUp, + }; +} diff --git a/autogpt_platform/frontend/src/components/monitor/FlowInfo.tsx b/autogpt_platform/frontend/src/components/monitor/FlowInfo.tsx index 05cc827dbb..fb9aae5d8c 100644 --- a/autogpt_platform/frontend/src/components/monitor/FlowInfo.tsx +++ b/autogpt_platform/frontend/src/components/monitor/FlowInfo.tsx @@ -271,12 +271,10 @@ export const FlowInfo: React.FC<
- We follow Tailwind CSS border radius system, which provides a - comprehensive set of radius values for different use cases. + We use a custom border radius system based on our Figma design + tokens, with simplified naming (xs, s, m, l, xl, 2xl, full) that + provides consistent radius values optimized for our design + system. @@ -174,11 +183,12 @@ export function AllVariants() { variant="h2" className="mb-2 text-xl font-semibold text-zinc-800" > - Complete Border Radius Scale + Design System Border Radius Tokens - All available border radius values in our design system. Each value - can be applied to all corners or specific corners/sides. + All border radius values from our Figma design tokens. Each value + can be applied to all corners or specific corners/sides using our + simplified naming convention. @@ -221,28 +231,31 @@ export function AllVariants() { No rounding (0px) -
Small rounding (2px)
-
Default rounding (4px)
-
Medium rounding (6px)
-
Large rounding (8px)
-
Extra large rounding (12px)
-
2X large rounding (16px)
-
3X large rounding (24px)
-
Fully rounded (circular)
+ code={`// Border radius examples - Design System Tokens +
Extra small rounding (4px)
+
Small rounding (8px)
+
Medium rounding (12px)
+
Large rounding (16px)
+
Extra large rounding (20px)
+
2X large rounding (24px)
+
Pill buttons (circular)
-// Directional rounding -
Top corners only
-
Right corners only
-
Bottom corners only
-
Left corners only
+// Directional rounding (works with all sizes) +
Top corners only
+
Right corners only
+
Bottom corners only
+
Left corners only
// Individual corners -
Top-left corner
-
Top-right corner
-
Bottom-left corner
-
Bottom-right corner
`} +
Top-left corner
+
Top-right corner
+
Bottom-left corner
+
Bottom-right corner
+ +// Usage recommendations + +
Card Container
+Input Field`} /> diff --git a/autogpt_platform/frontend/src/components/type-based-input.tsx b/autogpt_platform/frontend/src/components/type-based-input.tsx index 5a628a5935..491385234d 100644 --- a/autogpt_platform/frontend/src/components/type-based-input.tsx +++ b/autogpt_platform/frontend/src/components/type-based-input.tsx @@ -21,8 +21,20 @@ import { SelectContent, SelectItem, } from "@/components/ui/select"; -import { determineDataType, DataType } from "@/lib/autogpt-server-api/types"; -import { BlockIOSubSchema } from "@/lib/autogpt-server-api/types"; +import { + MultiSelector, + MultiSelectorContent, + MultiSelectorInput, + MultiSelectorItem, + MultiSelectorList, + MultiSelectorTrigger, +} from "@/components/ui/multiselect"; +import { + BlockIOObjectSubSchema, + BlockIOSubSchema, + DataType, + determineDataType, +} from "@/lib/autogpt-server-api/types"; /** * A generic prop structure for the TypeBasedInput. @@ -37,7 +49,7 @@ export interface TypeBasedInputProps { onChange: (value: any) => void; } -const inputClasses = "min-h-11 rounded-full border px-4 py-2.5"; +const inputClasses = "min-h-11 rounded-[1.375rem] border px-4 py-2.5 bg-text"; function Input({ className, @@ -171,6 +183,46 @@ export const TypeBasedInput: FC< break; } + case DataType.MULTI_SELECT: + const _schema = schema as BlockIOObjectSubSchema; + + innerInputElement = ( + v) + .map(([k, _]) => k)} + onValuesChange={(values: string[]) => { + const allKeys = Object.keys(_schema.properties); + onChange( + Object.fromEntries( + allKeys.map((opt) => [opt, values.includes(opt)]), + ), + ); + }} + > + + + + + + {Object.keys(_schema.properties) + .map((key) => ({ ..._schema.properties[key], key })) + .map(({ key, title, description }) => ( + + {title ?? key} + + ))} + + + + ); + break; + case DataType.SHORT_TEXT: default: innerInputElement = ( diff --git a/autogpt_platform/frontend/src/components/ui/button.tsx b/autogpt_platform/frontend/src/components/ui/button.tsx index 2239996ac8..3d4a915646 100644 --- a/autogpt_platform/frontend/src/components/ui/button.tsx +++ b/autogpt_platform/frontend/src/components/ui/button.tsx @@ -13,6 +13,7 @@ const buttonVariants = cva( "bg-neutral-900 text-neutral-50 shadow hover:bg-neutral-900/90 dark:bg-neutral-50 dark:text-neutral-900 dark:hover:bg-neutral-50/90", destructive: "bg-red-500 text-neutral-50 shadow-sm hover:bg-red-500/90 dark:bg-red-900 dark:text-neutral-50 dark:hover:bg-red-900/90", + accent: "bg-accent text-accent-foreground hover:bg-violet-500", outline: "border border-neutral-200 bg-white shadow-sm hover:bg-neutral-100 hover:text-neutral-900 dark:border-neutral-800 dark:bg-neutral-950 dark:hover:bg-neutral-800 dark:hover:text-neutral-50", secondary: diff --git a/autogpt_platform/frontend/src/components/ui/icons.tsx b/autogpt_platform/frontend/src/components/ui/icons.tsx index 5592ad05cf..2f7262d98b 100644 --- a/autogpt_platform/frontend/src/components/ui/icons.tsx +++ b/autogpt_platform/frontend/src/components/ui/icons.tsx @@ -1308,21 +1308,21 @@ export const IconTiktok = createIcon((props) => ( )); /** - * Close (X) icon component. + * Cross (X) icon component. * - * @component IconClose + * @component IconCross * @param {IconProps} props - The props object containing additional attributes and event handlers for the icon. - * @returns {JSX.Element} - The close icon. + * @returns {JSX.Element} - The cross icon. * * @example * // Default usage - * + * * * @example * // With custom color and size - * + * */ -export const IconClose = createIcon((props) => ( +export const IconCross = createIcon((props) => ( { return match && match[7].length === 11 ? match[7] : null; }; +const isValidMediaUri = (url: string): boolean => { + if (url.startsWith("data:")) { + return true; + } + const validUrl = /^(https?:\/\/)(www\.)?/i; + const cleanedUrl = url.split("?")[0]; + return validUrl.test(cleanedUrl); +}; + const isValidVideoUrl = (url: string): boolean => { if (url.startsWith("data:video")) { return true; } - const validUrl = /^(https?:\/\/)(www\.)?/i; const videoExtensions = /\.(mp4|webm|ogg)$/i; const youtubeRegex = /^(https?:\/\/)?(www\.)?(youtube\.com|youtu\.?be)\/.+$/; const cleanedUrl = url.split("?")[0]; return ( - (validUrl.test(cleanedUrl) && videoExtensions.test(cleanedUrl)) || + (isValidMediaUri(url) && videoExtensions.test(cleanedUrl)) || youtubeRegex.test(cleanedUrl) ); }; @@ -29,17 +37,16 @@ const isValidImageUrl = (url: string): boolean => { } const imageExtensions = /\.(jpeg|jpg|gif|png|svg|webp)$/i; const cleanedUrl = url.split("?")[0]; - return imageExtensions.test(cleanedUrl); + return isValidMediaUri(url) && imageExtensions.test(cleanedUrl); }; const isValidAudioUrl = (url: string): boolean => { if (url.startsWith("data:audio")) { return true; } - const validUrl = /^(https?:\/\/)(www\.)?/i; const audioExtensions = /\.(mp3|wav)$/i; const cleanedUrl = url.split("?")[0]; - return validUrl.test(cleanedUrl) && audioExtensions.test(cleanedUrl); + return isValidMediaUri(url) && audioExtensions.test(cleanedUrl); }; const VideoRenderer: React.FC<{ videoUrl: string }> = ({ videoUrl }) => { diff --git a/autogpt_platform/frontend/src/hooks/useAgentGraph.tsx b/autogpt_platform/frontend/src/hooks/useAgentGraph.tsx index 55b7c4d2bd..1cde0ccd58 100644 --- a/autogpt_platform/frontend/src/hooks/useAgentGraph.tsx +++ b/autogpt_platform/frontend/src/hooks/useAgentGraph.tsx @@ -1073,16 +1073,21 @@ export default function useAgentGraph( // runs after saving cron expression and inputs (if exists) const scheduleRunner = useCallback( - async (cronExpression: string, inputs: InputItem[]) => { + async ( + cronExpression: string, + inputs: InputItem[], + scheduleName: string, + ) => { await saveAgent(); try { if (flowID) { - await api.createSchedule({ + await api.createGraphExecutionSchedule({ graph_id: flowID, // flowVersion is always defined here because scheduling is opened for a specific version graph_version: flowVersion!, + name: scheduleName, cron: cronExpression, - input_data: inputs.reduce( + inputs: inputs.reduce( (acc, input) => ({ ...acc, [input.hardcodedValues.name]: input.hardcodedValues.value, diff --git a/autogpt_platform/frontend/src/hooks/useCredentials.ts b/autogpt_platform/frontend/src/hooks/useCredentials.ts index 750b9f857e..da29b85b1b 100644 --- a/autogpt_platform/frontend/src/hooks/useCredentials.ts +++ b/autogpt_platform/frontend/src/hooks/useCredentials.ts @@ -9,6 +9,7 @@ import { BlockIOCredentialsSubSchema, CredentialsProviderName, } from "@/lib/autogpt-server-api"; +import { getHostFromUrl } from "@/lib/utils/url"; export type CredentialsData = | { @@ -17,14 +18,18 @@ export type CredentialsData = supportsApiKey: boolean; supportsOAuth2: boolean; supportsUserPassword: boolean; + supportsHostScoped: boolean; isLoading: true; + discriminatorValue?: string; } | (CredentialsProviderData & { schema: BlockIOCredentialsSubSchema; supportsApiKey: boolean; supportsOAuth2: boolean; supportsUserPassword: boolean; + supportsHostScoped: boolean; isLoading: false; + discriminatorValue?: string; }); export default function useCredentials( @@ -33,12 +38,16 @@ export default function useCredentials( ): CredentialsData | null { const allProviders = useContext(CredentialsProvidersContext); - const discriminatorValue: CredentialsProviderName | null = - (credsInputSchema.discriminator && - credsInputSchema.discriminator_mapping![ - getValue(credsInputSchema.discriminator, nodeInputValues) - ]) || - null; + const discriminatorValue = [ + credsInputSchema.discriminator + ? getValue(credsInputSchema.discriminator, nodeInputValues) + : null, + ...(credsInputSchema.discriminator_values || []), + ].find(Boolean); + + const discriminatedProvider = credsInputSchema.discriminator_mapping + ? credsInputSchema.discriminator_mapping[discriminatorValue] + : null; let providerName: CredentialsProviderName; if (credsInputSchema.credentials_provider.length > 1) { @@ -47,14 +56,14 @@ export default function useCredentials( "Multi-provider credential input requires discriminator!", ); } - if (!discriminatorValue) { - console.log( + if (!discriminatedProvider) { + console.warn( `Missing discriminator value from '${credsInputSchema.discriminator}': ` + "hiding credentials input until it is set.", ); return null; } - providerName = discriminatorValue; + providerName = discriminatedProvider; } else { providerName = credsInputSchema.credentials_provider[0]; } @@ -69,6 +78,8 @@ export default function useCredentials( const supportsOAuth2 = credsInputSchema.credentials_types.includes("oauth2"); const supportsUserPassword = credsInputSchema.credentials_types.includes("user_password"); + const supportsHostScoped = + credsInputSchema.credentials_types.includes("host_scoped"); // No provider means maybe it's still loading if (!provider) { @@ -82,15 +93,24 @@ export default function useCredentials( return null; } - // Filter by OAuth credentials that have sufficient scopes for this block - const requiredScopes = credsInputSchema.credentials_scopes; - const savedCredentials = requiredScopes - ? provider.savedCredentials.filter( - (c) => - c.type != "oauth2" || - new Set(c.scopes).isSupersetOf(new Set(requiredScopes)), - ) - : provider.savedCredentials; + const savedCredentials = provider.savedCredentials.filter((c) => { + // Filter by OAuth credentials that have sufficient scopes for this block + if (c.type === "oauth2") { + const requiredScopes = credsInputSchema.credentials_scopes; + return ( + !requiredScopes || + new Set(c.scopes).isSupersetOf(new Set(requiredScopes)) + ); + } + + // Filter host_scoped credentials by host matching + if (c.type === "host_scoped") { + return discriminatorValue && getHostFromUrl(discriminatorValue) == c.host; + } + + // Include all other credential types + return true; + }); return { ...provider, @@ -99,7 +119,9 @@ export default function useCredentials( supportsApiKey, supportsOAuth2, supportsUserPassword, + supportsHostScoped, savedCredentials, + discriminatorValue, isLoading: false, }; } diff --git a/autogpt_platform/frontend/src/lib/autogpt-server-api/client.ts b/autogpt_platform/frontend/src/lib/autogpt-server-api/client.ts index f82ed8e302..69bc3e7ead 100644 --- a/autogpt_platform/frontend/src/lib/autogpt-server-api/client.ts +++ b/autogpt_platform/frontend/src/lib/autogpt-server-api/client.ts @@ -1,6 +1,8 @@ +import { getWebSocketToken } from "@/lib/supabase/actions"; import { getServerSupabase } from "@/lib/supabase/server/getServerSupabase"; import { createBrowserClient } from "@supabase/ssr"; import type { SupabaseClient } from "@supabase/supabase-js"; +import { proxyApiRequest, proxyFileUpload } from "./proxy-action"; import type { AddUserCreditsResponse, AnalyticsDetails, @@ -60,6 +62,7 @@ import type { User, UserOnboarding, UserPasswordCredentials, + HostScopedCredentials, UsersBalanceHistoryResponse, } from "./types"; @@ -345,6 +348,16 @@ export default class BackendAPI { ); } + createHostScopedCredentials( + credentials: Omit, + ): Promise { + return this._request( + "POST", + `/integrations/${credentials.provider}/credentials`, + { ...credentials, type: "host_scoped" }, + ); + } + listCredentials(provider?: string): Promise { return this._get( provider @@ -622,6 +635,15 @@ export default class BackendAPI { return this._get(`/library/agents/marketplace/${storeListingVersionId}`); } + getLibraryAgentByGraphID( + graphID: GraphID, + graphVersion?: number, + ): Promise { + return this._get(`/library/agents/by-graph/${graphID}`, { + version: graphVersion, + }); + } + addMarketplaceAgentToLibrary( storeListingVersionID: string, ): Promise { @@ -630,22 +652,43 @@ export default class BackendAPI { }); } - async updateLibraryAgent( + updateLibraryAgent( libraryAgentId: LibraryAgentID, params: { auto_update_version?: boolean; is_favorite?: boolean; is_archived?: boolean; - is_deleted?: boolean; }, - ): Promise { - await this._request("PUT", `/library/agents/${libraryAgentId}`, params); + ): Promise { + return this._request("PATCH", `/library/agents/${libraryAgentId}`, params); + } + + async deleteLibraryAgent(libraryAgentId: LibraryAgentID): Promise { + await this._request("DELETE", `/library/agents/${libraryAgentId}`); } forkLibraryAgent(libraryAgentId: LibraryAgentID): Promise { return this._request("POST", `/library/agents/${libraryAgentId}/fork`); } + async setupAgentTrigger( + libraryAgentID: LibraryAgentID, + params: { + name: string; + description?: string; + trigger_config: Record; + agent_credentials: Record; + }, + ): Promise { + return parseLibraryAgentPresetTimestamp( + await this._request( + "POST", + `/library/agents/${libraryAgentID}/setup-trigger`, + params, + ), + ); + } + async listLibraryAgentPresets(params?: { graph_id?: GraphID; page?: number; @@ -697,14 +740,10 @@ export default class BackendAPI { executeLibraryAgentPreset( presetID: LibraryAgentPresetID, - graphID: GraphID, - graphVersion: number, - nodeInput: { [key: string]: any }, + inputs?: { [key: string]: any }, ): Promise<{ id: GraphExecutionID }> { return this._request("POST", `/library/presets/${presetID}/execute`, { - graph_id: graphID, - graph_version: graphVersion, - node_input: nodeInput, + inputs, }); } @@ -712,22 +751,35 @@ export default class BackendAPI { /////////// SCHEDULES //////////// ////////////////////////////////// - async createSchedule(schedule: ScheduleCreatable): Promise { - return this._request("POST", `/schedules`, schedule).then( - parseScheduleTimestamp, + async createGraphExecutionSchedule( + params: ScheduleCreatable, + ): Promise { + return this._request( + "POST", + `/graphs/${params.graph_id}/schedules`, + params, + ).then(parseScheduleTimestamp); + } + + async listGraphExecutionSchedules(graphID: GraphID): Promise { + return this._get(`/graphs/${graphID}/schedules`).then((schedules) => + schedules.map(parseScheduleTimestamp), ); } - async deleteSchedule(scheduleId: ScheduleID): Promise<{ id: string }> { - return this._request("DELETE", `/schedules/${scheduleId}`); - } - - async listSchedules(): Promise { + /** @deprecated only used in legacy `Monitor` */ + async listAllGraphsExecutionSchedules(): Promise { return this._get(`/schedules`).then((schedules) => schedules.map(parseScheduleTimestamp), ); } + async deleteGraphExecutionSchedule( + scheduleID: ScheduleID, + ): Promise<{ id: ScheduleID }> { + return this._request("DELETE", `/schedules/${scheduleID}`); + } + ////////////////////////////////// ////////////// OTTO ////////////// ////////////////////////////////// @@ -744,50 +796,25 @@ export default class BackendAPI { return this._request("GET", path, query); } + private async getAuthToken(): Promise { + // Only try client-side session (for WebSocket connections) + // This will return "no-token-found" with httpOnly cookies, which is expected + const supabaseClient = await this.getSupabaseClient(); + const { + data: { session }, + } = (await supabaseClient?.auth.getSession()) || { + data: { session: null }, + }; + + return session?.access_token || "no-token-found"; + } + private async _uploadFile(path: string, file: File): Promise { - // Get session with retry logic - let token = "no-token-found"; - let retryCount = 0; - const maxRetries = 3; - - while (retryCount < maxRetries) { - const supabaseClient = await this.getSupabaseClient(); - const { - data: { session }, - } = (await supabaseClient?.auth.getSession()) || { - data: { session: null }, - }; - - if (session?.access_token) { - token = session.access_token; - break; - } - - retryCount++; - if (retryCount < maxRetries) { - await new Promise((resolve) => setTimeout(resolve, 100 * retryCount)); - } - } - - // Create a FormData object and append the file const formData = new FormData(); formData.append("file", file); - const response = await fetch(this.baseUrl + path, { - method: "POST", - headers: { - ...(token && { Authorization: `Bearer ${token}` }), - }, - body: formData, - }); - - if (!response.ok) { - throw new Error(`Error uploading file: ${response.statusText}`); - } - - // Parse the response appropriately - const media_url = await response.text(); - return media_url; + // Use proxy server action for secure file upload + return await proxyFileUpload(path, formData, this.baseUrl); } private async _request( @@ -799,103 +826,13 @@ export default class BackendAPI { console.debug(`${method} ${path} payload:`, payload); } - // Get session with retry logic - let token = "no-token-found"; - let retryCount = 0; - const maxRetries = 3; - - while (retryCount < maxRetries) { - const supabaseClient = await this.getSupabaseClient(); - const { - data: { session }, - } = (await supabaseClient?.auth.getSession()) || { - data: { session: null }, - }; - - if (session?.access_token) { - token = session.access_token; - break; - } - - retryCount++; - if (retryCount < maxRetries) { - await new Promise((resolve) => setTimeout(resolve, 100 * retryCount)); - } - } - - let url = this.baseUrl + path; - const payloadAsQuery = ["GET", "DELETE"].includes(method); - if (payloadAsQuery && payload) { - // For GET requests, use payload as query - const queryParams = new URLSearchParams(payload); - url += `?${queryParams.toString()}`; - } - - const hasRequestBody = !payloadAsQuery && payload !== undefined; - const response = await fetch(url, { + // Always use proxy server action to not expose any auth tokens to the browser + return await proxyApiRequest({ method, - headers: { - ...(hasRequestBody && { "Content-Type": "application/json" }), - ...(token && { Authorization: `Bearer ${token}` }), - }, - body: hasRequestBody ? JSON.stringify(payload) : undefined, + path, + payload, + baseUrl: this.baseUrl, }); - - if (!response.ok) { - console.warn(`${method} ${path} returned non-OK response:`, response); - - // console.warn("baseClient is attempting to redirect by changing window location") - // if ( - // response.status === 403 && - // response.statusText === "Not authenticated" && - // typeof window !== "undefined" // Check if in browser environment - // ) { - // window.location.href = "/login"; - // } - - let errorDetail; - try { - const errorData = await response.json(); - if ( - Array.isArray(errorData.detail) && - errorData.detail.length > 0 && - errorData.detail[0].loc - ) { - // This appears to be a Pydantic validation error - const errors = errorData.detail.map( - (err: _PydanticValidationError) => { - const location = err.loc.join(" -> "); - return `${location}: ${err.msg}`; - }, - ); - errorDetail = errors.join("\n"); - } else { - errorDetail = errorData.detail || response.statusText; - } - } catch { - errorDetail = response.statusText; - } - - throw new Error(errorDetail); - } - - // Handle responses with no content (like DELETE requests) - if ( - response.status === 204 || - response.headers.get("Content-Length") === "0" - ) { - return null; - } - - try { - return await response.json(); - } catch (e) { - if (e instanceof SyntaxError) { - console.warn(`${method} ${path} returned invalid JSON:`, e); - return null; - } - throw e; - } } //////////////////////////////////////// @@ -983,10 +920,19 @@ export default class BackendAPI { async connectWebSocket(): Promise { return (this.wsConnecting ??= new Promise(async (resolve, reject) => { try { - const supabaseClient = await this.getSupabaseClient(); - const token = - (await supabaseClient?.auth.getSession())?.data.session - ?.access_token || ""; + let token = ""; + try { + const { token: serverToken, error } = await getWebSocketToken(); + if (serverToken && !error) { + token = serverToken; + } else if (error) { + console.warn("Failed to get WebSocket token from server:", error); + } + } catch (error) { + console.warn("Failed to get token for WebSocket connection:", error); + // Continue with empty token, connection might still work + } + const wsUrlWithToken = `${this.wsUrl}?token=${token}`; this.webSocket = new WebSocket(wsUrlWithToken); this.webSocket.state = "connecting"; diff --git a/autogpt_platform/frontend/src/lib/autogpt-server-api/context.tsx b/autogpt_platform/frontend/src/lib/autogpt-server-api/context.tsx index 3a9aa5d7f9..f37a2e057e 100644 --- a/autogpt_platform/frontend/src/lib/autogpt-server-api/context.tsx +++ b/autogpt_platform/frontend/src/lib/autogpt-server-api/context.tsx @@ -3,6 +3,13 @@ import BackendAPI from "./client"; import React, { createContext, useMemo } from "react"; +// Add window.api type declaration for global access +declare global { + interface Window { + api?: BackendAPI; + } +} + const BackendAPIProviderContext = createContext(null); export function BackendAPIProvider({ @@ -12,6 +19,13 @@ export function BackendAPIProvider({ }): React.ReactNode { const api = useMemo(() => new BackendAPI(), []); + if ( + process.env.NEXT_PUBLIC_BEHAVE_AS == "LOCAL" && + typeof window !== "undefined" + ) { + window.api = api; // Expose the API globally for debugging purposes + } + return ( {children} diff --git a/autogpt_platform/frontend/src/lib/autogpt-server-api/helpers.ts b/autogpt_platform/frontend/src/lib/autogpt-server-api/helpers.ts new file mode 100644 index 0000000000..c9ee05d0e3 --- /dev/null +++ b/autogpt_platform/frontend/src/lib/autogpt-server-api/helpers.ts @@ -0,0 +1,236 @@ +import { getServerSupabase } from "@/lib/supabase/server/getServerSupabase"; + +export function buildRequestUrl( + baseUrl: string, + path: string, + method: string, + payload?: Record, +): string { + let url = baseUrl + path; + const payloadAsQuery = ["GET", "DELETE"].includes(method); + + if (payloadAsQuery && payload) { + const queryParams = new URLSearchParams(payload); + url += `?${queryParams.toString()}`; + } + + return url; +} + +export async function getServerAuthToken(): Promise { + const supabase = await getServerSupabase(); + + if (!supabase) { + throw new Error("Supabase client not available"); + } + + try { + const { + data: { session }, + error, + } = await supabase.auth.getSession(); + + if (error || !session?.access_token) { + return "no-token-found"; + } + + return session.access_token; + } catch (error) { + console.error("Failed to get auth token:", error); + return "no-token-found"; + } +} + +export function createRequestHeaders( + token: string, + hasRequestBody: boolean, + contentType: string = "application/json", +): Record { + const headers: Record = {}; + + if (hasRequestBody) { + headers["Content-Type"] = contentType; + } + + if (token && token !== "no-token-found") { + headers["Authorization"] = `Bearer ${token}`; + } + + return headers; +} + +export function serializeRequestBody( + payload: any, + contentType: string = "application/json", +): string { + switch (contentType) { + case "application/json": + return JSON.stringify(payload); + case "application/x-www-form-urlencoded": + return new URLSearchParams(payload).toString(); + default: + // For custom content types, assume payload is already properly formatted + return typeof payload === "string" ? payload : JSON.stringify(payload); + } +} + +export async function parseApiError(response: Response): Promise { + try { + const errorData = await response.json(); + + if ( + Array.isArray(errorData.detail) && + errorData.detail.length > 0 && + errorData.detail[0].loc + ) { + // Pydantic validation error + const errors = errorData.detail.map((err: any) => { + const location = err.loc.join(" -> "); + return `${location}: ${err.msg}`; + }); + return errors.join("\n"); + } + + return errorData.detail || response.statusText; + } catch { + return response.statusText; + } +} + +export async function parseApiResponse(response: Response): Promise { + // Handle responses with no content + if ( + response.status === 204 || + response.headers.get("Content-Length") === "0" + ) { + return null; + } + + try { + return await response.json(); + } catch (e) { + if (e instanceof SyntaxError) { + return null; + } + throw e; + } +} + +function isAuthenticationError( + response: Response, + errorDetail: string, +): boolean { + return ( + response.status === 401 || + response.status === 403 || + errorDetail.toLowerCase().includes("not authenticated") || + errorDetail.toLowerCase().includes("unauthorized") || + errorDetail.toLowerCase().includes("authentication failed") + ); +} + +function isLogoutInProgress(): boolean { + if (typeof window === "undefined") return false; + + try { + // Check if logout was recently triggered + const logoutTimestamp = window.localStorage.getItem("supabase-logout"); + if (logoutTimestamp) { + const timeDiff = Date.now() - parseInt(logoutTimestamp); + // Consider logout in progress for 5 seconds after trigger + return timeDiff < 5000; + } + + // Check if we're being redirected to login + return ( + window.location.pathname.includes("/login") || + window.location.pathname.includes("/logout") + ); + } catch { + return false; + } +} + +export async function makeAuthenticatedRequest( + method: string, + url: string, + payload?: Record, + contentType: string = "application/json", +): Promise { + const token = await getServerAuthToken(); + const payloadAsQuery = ["GET", "DELETE"].includes(method); + const hasRequestBody = !payloadAsQuery && payload !== undefined; + + const response = await fetch(url, { + method, + headers: createRequestHeaders(token, hasRequestBody, contentType), + body: hasRequestBody + ? serializeRequestBody(payload, contentType) + : undefined, + }); + + if (!response.ok) { + const errorDetail = await parseApiError(response); + + // Handle authentication errors gracefully during logout + if (isAuthenticationError(response, errorDetail)) { + if (isLogoutInProgress()) { + // Silently return null during logout to prevent error noise + console.debug( + "Authentication request failed during logout, ignoring:", + errorDetail, + ); + return null; + } + + // For authentication errors outside logout, log but don't throw + // This prevents crashes when session expires naturally + console.warn("Authentication failed:", errorDetail); + return null; + } + + // For other errors, throw as normal + throw new Error(errorDetail); + } + + return parseApiResponse(response); +} + +export async function makeAuthenticatedFileUpload( + url: string, + formData: FormData, +): Promise { + const token = await getServerAuthToken(); + + const headers: Record = {}; + if (token && token !== "no-token-found") { + headers["Authorization"] = `Bearer ${token}`; + } + + // Don't set Content-Type for FormData - let the browser set it with boundary + const response = await fetch(url, { + method: "POST", + headers, + body: formData, + }); + + if (!response.ok) { + // Handle authentication errors gracefully for file uploads too + const errorMessage = `Error uploading file: ${response.statusText}`; + + if (response.status === 401 || response.status === 403) { + if (isLogoutInProgress()) { + console.debug( + "File upload authentication failed during logout, ignoring", + ); + return ""; + } + console.warn("File upload authentication failed:", errorMessage); + return ""; + } + + throw new Error(errorMessage); + } + + return await response.text(); +} diff --git a/autogpt_platform/frontend/src/lib/autogpt-server-api/proxy-action.ts b/autogpt_platform/frontend/src/lib/autogpt-server-api/proxy-action.ts new file mode 100644 index 0000000000..74a5d3de91 --- /dev/null +++ b/autogpt_platform/frontend/src/lib/autogpt-server-api/proxy-action.ts @@ -0,0 +1,51 @@ +"use server"; + +import * as Sentry from "@sentry/nextjs"; +import { + buildRequestUrl, + makeAuthenticatedFileUpload, + makeAuthenticatedRequest, +} from "./helpers"; + +const DEFAULT_BASE_URL = "http://localhost:8006/api"; + +export interface ProxyRequestOptions { + method: "GET" | "POST" | "PUT" | "PATCH" | "DELETE"; + path: string; + payload?: Record; + baseUrl?: string; + contentType?: string; +} + +export async function proxyApiRequest({ + method, + path, + payload, + baseUrl = process.env.NEXT_PUBLIC_AGPT_SERVER_URL || DEFAULT_BASE_URL, + contentType = "application/json", +}: ProxyRequestOptions) { + return await Sentry.withServerActionInstrumentation( + "proxyApiRequest", + {}, + async () => { + const url = buildRequestUrl(baseUrl, path, method, payload); + return makeAuthenticatedRequest(method, url, payload, contentType); + }, + ); +} + +export async function proxyFileUpload( + path: string, + formData: FormData, + baseUrl = process.env.NEXT_PUBLIC_AGPT_SERVER_URL || + "http://localhost:8006/api", +): Promise { + return await Sentry.withServerActionInstrumentation( + "proxyFileUpload", + {}, + async () => { + const url = baseUrl + path; + return makeAuthenticatedFileUpload(url, formData); + }, + ); +} diff --git a/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts b/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts index b4ba851053..e85e326c3b 100644 --- a/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts +++ b/autogpt_platform/frontend/src/lib/autogpt-server-api/types.ts @@ -140,12 +140,17 @@ export type BlockIOBooleanSubSchema = BlockIOSubSchemaMeta & { secret?: boolean; }; -export type CredentialsType = "api_key" | "oauth2" | "user_password"; +export type CredentialsType = + | "api_key" + | "oauth2" + | "user_password" + | "host_scoped"; export type Credentials = | APIKeyCredentials | OAuth2Credentials - | UserPasswordCredentials; + | UserPasswordCredentials + | HostScopedCredentials; // --8<-- [start:BlockIOCredentialsSubSchema] export const PROVIDER_NAMES = { @@ -161,6 +166,7 @@ export const PROVIDER_NAMES = { GOOGLE: "google", GOOGLE_MAPS: "google_maps", GROQ: "groq", + HTTP: "http", HUBSPOT: "hubspot", IDEOGRAM: "ideogram", JINA: "jina", @@ -199,6 +205,7 @@ export type BlockIOCredentialsSubSchema = BlockIOObjectSubSchema & { credentials_types: Array; discriminator?: string; discriminator_mapping?: { [key: string]: CredentialsProviderName }; + discriminator_values?: any[]; secret?: boolean; }; @@ -401,11 +408,29 @@ export type LibraryAgent = { updated_at: Date; name: string; description: string; - input_schema: BlockIOObjectSubSchema; + input_schema: GraphIOSchema; + credentials_input_schema: { + type: "object"; + properties: { [key: string]: BlockIOCredentialsSubSchema }; + required: (keyof LibraryAgent["credentials_input_schema"]["properties"])[]; + }; new_output: boolean; can_access_graph: boolean; is_latest_version: boolean; -}; +} & ( + | { + has_external_trigger: true; + trigger_setup_info: { + provider: CredentialsProviderName; + config_schema: BlockIORootSchema; + credentials_input_name?: string; + }; + } + | { + has_external_trigger: false; + trigger_setup_info?: null; + } +); export type LibraryAgentID = Brand; @@ -432,9 +457,11 @@ export type LibraryAgentPreset = { graph_id: GraphID; graph_version: number; inputs: { [key: string]: any }; + credentials: Record; name: string; description: string; is_active: boolean; + webhook_id?: string; }; export type LibraryAgentPresetID = Brand; @@ -481,6 +508,7 @@ export type CredentialsMetaResponse = { title?: string; scopes?: Array; username?: string; + host?: string; }; /* Mirror of backend/server/integrations/router.py:CredentialsDeletionResponse */ @@ -539,6 +567,14 @@ export type UserPasswordCredentials = BaseCredentials & { password: string; }; +/* Mirror of backend/backend/data/model.py:HostScopedCredentials */ +export type HostScopedCredentials = BaseCredentials & { + type: "host_scoped"; + title: string; + host: string; + headers: Record; +}; + // Mirror of backend/backend/data/notifications.py:NotificationType export type NotificationType = | "AGENT_RUN" @@ -735,6 +771,7 @@ export type ProfileDetails = { avatar_url: string; }; +/* Mirror of backend/executor/scheduler.py:GraphExecutionJobInfo */ export type Schedule = { id: ScheduleID; name: string; @@ -742,17 +779,21 @@ export type Schedule = { user_id: UserID; graph_id: GraphID; graph_version: number; - input_data: { [key: string]: any }; + input_data: Record; + input_credentials: Record; next_run_time: Date; }; export type ScheduleID = Brand; +/* Mirror of backend/server/routers/v1.py:ScheduleCreationRequest */ export type ScheduleCreatable = { - cron: string; graph_id: GraphID; graph_version: number; - input_data: { [key: string]: any }; + name: string; + cron: string; + inputs: Record; + credentials?: Record; }; export type MyAgent = { diff --git a/autogpt_platform/frontend/src/lib/autogpt-server-api/utils.ts b/autogpt_platform/frontend/src/lib/autogpt-server-api/utils.ts index a0d2262c28..4c7e9b9bca 100644 --- a/autogpt_platform/frontend/src/lib/autogpt-server-api/utils.ts +++ b/autogpt_platform/frontend/src/lib/autogpt-server-api/utils.ts @@ -1,14 +1,15 @@ import { Connection } from "@xyflow/react"; -import { Graph, Block, BlockUIType, Link } from "./types"; +import { Block, BlockUIType, Link } from "./types"; +import { Graph } from "@/app/api/__generated__/models/graph"; export function removeAgentInputBlockValues(graph: Graph, blocks: Block[]) { - const inputBlocks = graph.nodes.filter( + const inputBlocks = graph.nodes?.filter( (node) => blocks.find((b) => b.id === node.block_id)?.uiType === BlockUIType.INPUT, ); - const modifiedNodes = graph.nodes.map((node) => { - if (inputBlocks.find((inputNode) => inputNode.id === node.id)) { + const modifiedNodes = graph.nodes?.map((node) => { + if (inputBlocks?.find((inputNode) => inputNode.id === node.id)) { return { ...node, input_default: { @@ -61,7 +62,7 @@ function removeCredentials(obj: any): void { */ function updateBlockIDs(graph: Graph) { graph.nodes - .filter((node) => node.block_id in updatedBlockIDMap) + ?.filter((node) => node.block_id in updatedBlockIDMap) .forEach((node) => { node.block_id = updatedBlockIDMap[node.block_id]; }); diff --git a/autogpt_platform/frontend/src/lib/cron-expression-utils.ts b/autogpt_platform/frontend/src/lib/cron-expression-utils.ts new file mode 100644 index 0000000000..7a85082cc4 --- /dev/null +++ b/autogpt_platform/frontend/src/lib/cron-expression-utils.ts @@ -0,0 +1,265 @@ +export type CronFrequency = + | "every minute" + | "hourly" + | "daily" + | "weekly" + | "monthly" + | "yearly" + | "custom"; + +export type CronExpressionParams = + | { frequency: "every minute" } + | { + frequency: "hourly"; + minute: number; + } + | (( + | { + frequency: "daily"; + } + | { + frequency: "weekly"; + /** 0-based list of weekdays: 0 = Monday ... 6 = Sunday */ + days: number[]; + } + | { + frequency: "monthly"; + /** 1-based list of month days */ + days: number[]; + } + | { + frequency: "yearly"; + /** 1-based list of months (1-12) */ + months: number[]; + } + | { + frequency: "custom"; + customInterval: { unit: string; value: number }; + } + ) & { + minute: number; + hour: number; + }); + +export function makeCronExpression(params: CronExpressionParams): string { + const frequency = params.frequency; + + if (frequency === "every minute") return "* * * * *"; + if (frequency === "hourly") return `${params.minute} * * * *`; + if (frequency === "daily") return `${params.minute} ${params.hour} * * *`; + if (frequency === "weekly") { + const { minute, hour, days } = params; + const weekDaysExpr = days.sort((a, b) => a - b).join(","); + return `${minute} ${hour} * * ${weekDaysExpr}`; + } + if (frequency === "monthly") { + const { minute, hour, days } = params; + const monthDaysExpr = days.sort((a, b) => a - b).join(","); + return `${minute} ${hour} ${monthDaysExpr} * *`; + } + if (frequency === "yearly") { + const { minute, hour, months } = params; + const monthList = months.sort((a, b) => a - b).join(","); + return `${minute} ${hour} 1 ${monthList} *`; + } + if (frequency === "custom") { + const { minute, hour, customInterval } = params; + if (customInterval.unit === "minutes") { + return `*/${customInterval.value} * * * *`; + } else if (customInterval.unit === "hours") { + return `0 */${customInterval.value} * * *`; + } else { + return `${minute} ${hour} */${customInterval.value} * *`; + } + } + + return ""; +} + +export function humanizeCronExpression(cronExpression: string): string { + const parts = cronExpression.trim().split(/\s+/); + if (parts.length !== 5) { + throw new Error("Invalid cron expression format."); + } + + const [minute, hour, dayOfMonth, month, dayOfWeek] = parts; + + // Handle every minute + if (cronExpression === "* * * * *") { + return "Every minute"; + } + + // Handle minute intervals (e.g., */5 * * * *) + if ( + minute.startsWith("*/") && + hour === "*" && + dayOfMonth === "*" && + month === "*" && + dayOfWeek === "*" + ) { + const interval = minute.substring(2); + return `Every ${interval} minutes`; + } + + // Handle hour intervals (e.g., 30 * * * *) + if ( + hour === "*" && + !minute.includes("/") && + dayOfMonth === "*" && + month === "*" && + dayOfWeek === "*" + ) { + return `Every hour at minute ${minute}`; + } + + // Handle every N hours (e.g., 0 */2 * * *) + if ( + hour.startsWith("*/") && + minute === "0" && + dayOfMonth === "*" && + month === "*" && + dayOfWeek === "*" + ) { + const interval = hour.substring(2); + return `Every ${interval} hours`; + } + + // Handle daily (e.g., 30 14 * * *) + if ( + dayOfMonth === "*" && + month === "*" && + dayOfWeek === "*" && + !minute.includes("/") && + !hour.includes("/") + ) { + return `Every day at ${formatTime(hour, minute)}`; + } + + // Handle weekly (e.g., 30 14 * * 1,3,5) + if ( + dayOfWeek !== "*" && + dayOfMonth === "*" && + month === "*" && + !minute.includes("/") && + !hour.includes("/") + ) { + const days = getDayNames(dayOfWeek); + return `Every ${days} at ${formatTime(hour, minute)}`; + } + + // Handle monthly (e.g., 30 14 1,15 * *) + if ( + dayOfMonth !== "*" && + month === "*" && + dayOfWeek === "*" && + !minute.includes("/") && + !hour.includes("/") + ) { + const days = dayOfMonth.split(",").map(Number); + const dayList = days.join(", "); + return `On day ${dayList} of every month at ${formatTime(hour, minute)}`; + } + + // Handle yearly (e.g., 30 14 1 1,6,12 *) + if ( + dayOfMonth !== "*" && + month !== "*" && + dayOfWeek === "*" && + !minute.includes("/") && + !hour.includes("/") + ) { + const months = getMonthNames(month); + return `Every year on the 1st day of ${months} at ${formatTime(hour, minute)}`; + } + + // Handle custom minute intervals with other fields as * (e.g., every N minutes) + if ( + minute.includes("/") && + hour === "*" && + dayOfMonth === "*" && + month === "*" && + dayOfWeek === "*" + ) { + const interval = minute.split("/")[1]; + return `Every ${interval} minutes`; + } + + // Handle custom hour intervals with other fields as * (e.g., every N hours) + if ( + hour.includes("/") && + minute === "0" && + dayOfMonth === "*" && + month === "*" && + dayOfWeek === "*" + ) { + const interval = hour.split("/")[1]; + return `Every ${interval} hours`; + } + + // Handle specific days with custom intervals (e.g., every N days) + if ( + dayOfMonth.startsWith("*/") && + month === "*" && + dayOfWeek === "*" && + !minute.includes("/") && + !hour.includes("/") + ) { + const interval = dayOfMonth.substring(2); + return `Every ${interval} days at ${formatTime(hour, minute)}`; + } + + return `Cron Expression: ${cronExpression}`; +} + +function formatTime(hour: string, minute: string): string { + const formattedHour = padZero(hour); + const formattedMinute = padZero(minute); + return `${formattedHour}:${formattedMinute}`; +} + +function padZero(value: string): string { + return value.padStart(2, "0"); +} + +function getDayNames(dayOfWeek: string): string { + const days = dayOfWeek.split(",").map(Number); + const dayNames = days + .map((d) => { + const names = [ + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + ]; + return names[d] || `Unknown(${d})`; + }) + .join(", "); + return dayNames; +} + +function getMonthNames(month: string): string { + const months = month.split(",").map(Number); + const monthNames = months + .map((m) => { + const names = [ + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December", + ]; + return names[m - 1] || `Unknown(${m})`; + }) + .join(", "); + return monthNames; +} diff --git a/autogpt_platform/frontend/src/lib/hooks/useBreakpoint.ts b/autogpt_platform/frontend/src/lib/hooks/useBreakpoint.ts new file mode 100644 index 0000000000..44fc52bac3 --- /dev/null +++ b/autogpt_platform/frontend/src/lib/hooks/useBreakpoint.ts @@ -0,0 +1,50 @@ +import { useEffect, useState } from "react"; + +export type Breakpoint = "base" | "sm" | "md" | "lg" | "xl" | "2xl"; + +// Explicitly maps to tailwind breakpoints +const breakpoints: Record = { + base: 0, + sm: 640, + md: 768, + lg: 1024, + xl: 1280, + "2xl": 1536, +}; + +export function useBreakpoint(): Breakpoint { + const [breakpoint, setBreakpoint] = useState("lg"); + + useEffect(() => { + const getBreakpoint = () => { + const width = window.innerWidth; + if (width < breakpoints.sm) return "base"; + if (width < breakpoints.md) return "sm"; + if (width < breakpoints.lg) return "md"; + if (width < breakpoints.xl) return "lg"; + if (width < breakpoints["2xl"]) return "xl"; + return "2xl"; + }; + + const handleResize = () => { + const current = getBreakpoint(); + setBreakpoint(current); + }; + + window.addEventListener("resize", handleResize); + handleResize(); // initial call + + return () => window.removeEventListener("resize", handleResize); + }, []); + + return breakpoint; +} + +export function isLargeScreen(bp: Breakpoint) { + if (bp === "sm") return false; + if (bp === "md") return false; + if (bp === "lg") return true; + if (bp === "xl") return true; + if (bp === "2xl") return true; + return false; +} diff --git a/autogpt_platform/frontend/src/lib/monitor/cronExpressionManager.ts b/autogpt_platform/frontend/src/lib/monitor/cronExpressionManager.ts deleted file mode 100644 index fb9fbe8eee..0000000000 --- a/autogpt_platform/frontend/src/lib/monitor/cronExpressionManager.ts +++ /dev/null @@ -1,239 +0,0 @@ -export class CronExpressionManager { - generateCronExpression( - frequency: string, - selectedTime: string, - selectedDays: number[], - selectedMinute: string, - customInterval: { unit: string; value: number }, - ): string { - const [hours, minutes] = selectedTime.split(":").map(Number); - let expression = ""; - - switch (frequency) { - case "minute": - expression = "* * * * *"; - break; - case "hour": - expression = `${selectedMinute} * * * *`; - break; - case "daily": - expression = `${minutes} ${hours} * * *`; - break; - case "weekly": - const days = selectedDays.join(","); - expression = `${minutes} ${hours} * * ${days}`; - break; - case "monthly": - const monthDays = selectedDays.sort((a, b) => a - b).join(","); - expression = `${minutes} ${hours} ${monthDays} * *`; - break; - case "yearly": - const monthList = selectedDays - .map((d) => d + 1) - .sort((a, b) => a - b) - .join(","); - expression = `${minutes} ${hours} 1 ${monthList} *`; - break; - case "custom": - if (customInterval.unit === "minutes") { - expression = `*/${customInterval.value} * * * *`; - } else if (customInterval.unit === "hours") { - expression = `0 */${customInterval.value} * * *`; - } else { - expression = `${minutes} ${hours} */${customInterval.value} * *`; - } - break; - default: - expression = ""; - } - return expression; - } - - generateDescription(cronExpression: string): string { - const parts = cronExpression.trim().split(/\s+/); - if (parts.length !== 5) { - throw new Error("Invalid cron expression format."); - } - - const [minute, hour, dayOfMonth, month, dayOfWeek] = parts; - - // Handle every minute - if (cronExpression === "* * * * *") { - return "Every minute"; - } - - // Handle minute intervals (e.g., */5 * * * *) - if ( - minute.startsWith("*/") && - hour === "*" && - dayOfMonth === "*" && - month === "*" && - dayOfWeek === "*" - ) { - const interval = minute.substring(2); - return `Every ${interval} minutes`; - } - - // Handle hour intervals (e.g., 30 * * * *) - if ( - hour === "*" && - !minute.includes("/") && - dayOfMonth === "*" && - month === "*" && - dayOfWeek === "*" - ) { - return `Every hour at minute ${minute}`; - } - - // Handle every N hours (e.g., 0 */2 * * *) - if ( - hour.startsWith("*/") && - minute === "0" && - dayOfMonth === "*" && - month === "*" && - dayOfWeek === "*" - ) { - const interval = hour.substring(2); - return `Every ${interval} hours`; - } - - // Handle daily (e.g., 30 14 * * *) - if ( - dayOfMonth === "*" && - month === "*" && - dayOfWeek === "*" && - !minute.includes("/") && - !hour.includes("/") - ) { - return `Every day at ${this.formatTime(hour, minute)}`; - } - - // Handle weekly (e.g., 30 14 * * 1,3,5) - if ( - dayOfWeek !== "*" && - dayOfMonth === "*" && - month === "*" && - !minute.includes("/") && - !hour.includes("/") - ) { - const days = this.getDayNames(dayOfWeek); - return `Every ${days} at ${this.formatTime(hour, minute)}`; - } - - // Handle monthly (e.g., 30 14 1,15 * *) - if ( - dayOfMonth !== "*" && - month === "*" && - dayOfWeek === "*" && - !minute.includes("/") && - !hour.includes("/") - ) { - const days = dayOfMonth.split(",").map(Number); - const dayList = days.join(", "); - return `On day ${dayList} of every month at ${this.formatTime(hour, minute)}`; - } - - // Handle yearly (e.g., 30 14 1 1,6,12 *) - if ( - dayOfMonth !== "*" && - month !== "*" && - dayOfWeek === "*" && - !minute.includes("/") && - !hour.includes("/") - ) { - const months = this.getMonthNames(month); - return `Every year on the 1st day of ${months} at ${this.formatTime(hour, minute)}`; - } - - // Handle custom minute intervals with other fields as * (e.g., every N minutes) - if ( - minute.includes("/") && - hour === "*" && - dayOfMonth === "*" && - month === "*" && - dayOfWeek === "*" - ) { - const interval = minute.split("/")[1]; - return `Every ${interval} minutes`; - } - - // Handle custom hour intervals with other fields as * (e.g., every N hours) - if ( - hour.includes("/") && - minute === "0" && - dayOfMonth === "*" && - month === "*" && - dayOfWeek === "*" - ) { - const interval = hour.split("/")[1]; - return `Every ${interval} hours`; - } - - // Handle specific days with custom intervals (e.g., every N days) - if ( - dayOfMonth.startsWith("*/") && - month === "*" && - dayOfWeek === "*" && - !minute.includes("/") && - !hour.includes("/") - ) { - const interval = dayOfMonth.substring(2); - return `Every ${interval} days at ${this.formatTime(hour, minute)}`; - } - - return `Cron Expression: ${cronExpression}`; - } - - private formatTime(hour: string, minute: string): string { - const formattedHour = this.padZero(hour); - const formattedMinute = this.padZero(minute); - return `${formattedHour}:${formattedMinute}`; - } - - private padZero(value: string): string { - return value.padStart(2, "0"); - } - - private getDayNames(dayOfWeek: string): string { - const days = dayOfWeek.split(",").map(Number); - const dayNames = days - .map((d) => { - const names = [ - "Sunday", - "Monday", - "Tuesday", - "Wednesday", - "Thursday", - "Friday", - "Saturday", - ]; - return names[d] || `Unknown(${d})`; - }) - .join(", "); - return dayNames; - } - - private getMonthNames(month: string): string { - const months = month.split(",").map(Number); - const monthNames = months - .map((m) => { - const names = [ - "January", - "February", - "March", - "April", - "May", - "June", - "July", - "August", - "September", - "October", - "November", - "December", - ]; - return names[m - 1] || `Unknown(${m})`; - }) - .join(", "); - return monthNames; - } -} diff --git a/autogpt_platform/frontend/src/lib/supabase/SESSION_VALIDATION.md b/autogpt_platform/frontend/src/lib/supabase/SESSION_VALIDATION.md new file mode 100644 index 0000000000..482ceb27fc --- /dev/null +++ b/autogpt_platform/frontend/src/lib/supabase/SESSION_VALIDATION.md @@ -0,0 +1,180 @@ +# Server-Side Session Validation with httpOnly Cookies + +This implementation ensures that Supabase session validation is always performed on the server side using httpOnly cookies for improved security. + +## Key Features + +- **httpOnly Cookies**: Session cookies are inaccessible to client-side JavaScript, preventing XSS attacks +- **Server-Side Authentication**: All API requests are authenticated on the server using httpOnly cookies +- **Automatic Request Proxying**: All BackendAPI requests are automatically proxied through server actions +- **File Upload Support**: File uploads work seamlessly with httpOnly cookie authentication +- **Zero Code Changes**: Existing BackendAPI usage continues to work without modifications +- **Cross-Tab Logout**: Logout events are still synchronized across browser tabs + +## How It Works + +All API requests made through `BackendAPI` are automatically proxied through server actions that: + +1. Retrieve the JWT token from server-side httpOnly cookies +2. Make the authenticated request to the backend API +3. Return the response to the client + +This includes both regular API calls and file uploads, all handled transparently! + +## Usage + +### Client Components + +No changes needed! The existing `useSupabase` hook and `useBackendAPI` continue to work: + +```tsx +"use client"; +import { useSupabase } from "@/lib/supabase/hooks/useSupabase"; +import { useBackendAPI } from "@/lib/autogpt-server-api/context"; + +function MyComponent() { + const { user, isLoggedIn, isUserLoading, logOut } = useSupabase(); + const api = useBackendAPI(); + + if (isUserLoading) return
Loading...
; + if (!isLoggedIn) return
Please log in
; + + // Regular API calls use secure server-side authentication + const handleGetGraphs = async () => { + const graphs = await api.listGraphs(); + console.log(graphs); + }; + + // File uploads also work with secure authentication + const handleFileUpload = async (file: File) => { + try { + const mediaUrl = await api.uploadStoreSubmissionMedia(file); + console.log("Uploaded:", mediaUrl); + } catch (error) { + console.error("Upload failed:", error); + } + }; + + return ( +
+

Welcome, {user?.email}!

+ + + e.target.files?.[0] && handleFileUpload(e.target.files[0]) + } + /> + +
+ ); +} +``` + +### Server Components + +No changes needed! Server components continue to work as before: + +```tsx +import { validateSession, getCurrentUser } from "@/lib/supabase/actions"; +import { redirect } from "next/navigation"; + +async function MyServerComponent() { + const { user, error } = await getCurrentUser(); + + if (error || !user) { + redirect("/login"); + } + + return
Welcome, {user.email}!
; +} +``` + +### Server Actions + +No changes needed! Server actions continue to work as before: + +```tsx +"use server"; +import { validateSession } from "@/lib/supabase/actions"; +import BackendAPI from "@/lib/autogpt-server-api"; +import { redirect } from "next/navigation"; + +export async function myServerAction() { + const { user, isValid } = await validateSession("/current-path"); + + if (!isValid || !user) { + redirect("/login"); + return; + } + + // This automatically uses secure server-side authentication + const api = new BackendAPI(); + const graphs = await api.listGraphs(); + + return graphs; +} +``` + +### API Calls and File Uploads + +All operations use the same simple code everywhere: + +```tsx +// Works the same in both client and server contexts +const api = new BackendAPI(); + +// Regular API requests +const graphs = await api.listGraphs(); +const user = await api.createUser(); +const onboarding = await api.getUserOnboarding(); + +// File uploads +const file = new File(["content"], "example.txt", { type: "text/plain" }); +const mediaUrl = await api.uploadStoreSubmissionMedia(file); +``` + +## Available Server Actions + +- `validateSession(currentPath)` - Validates the current session and returns user data +- `getCurrentUser()` - Gets the current user without path validation +- `serverLogout()` - Logs out the user server-side +- `refreshSession()` - Refreshes the current session + +## Internal Architecture + +### Request Flow + +All API requests (including file uploads) follow this flow: + +1. **Any API call**: `api.listGraphs()` or `api.uploadStoreSubmissionMedia(file)` +2. **Proxy server action**: `proxyApiRequest()` or `proxyFileUpload()` handles the request +3. **Server authentication**: Gets JWT from httpOnly cookies +4. **Backend request**: Makes authenticated request to backend API +5. **Response**: Returns data to the calling code + +### File Upload Implementation + +File uploads are handled through a dedicated `proxyFileUpload` server action that: + +- Receives the file data as FormData on the server +- Retrieves authentication tokens from httpOnly cookies +- Forwards the authenticated upload request to the backend +- Returns the upload result to the client + +## Security Benefits + +1. **XSS Protection**: httpOnly cookies can't be accessed by malicious scripts +2. **CSRF Protection**: Combined with SameSite cookie settings +3. **Server-Side Validation**: Session validation always happens on the trusted server +4. **Zero Token Exposure**: JWT tokens never exposed to client-side JavaScript +5. **Zero Attack Surface**: No client-side session manipulation possible +6. **Secure File Uploads**: File uploads maintain the same security model + +## Migration Notes + +- **No code changes required** - all existing BackendAPI usage continues to work +- File uploads now work seamlessly with httpOnly cookies +- Cross-tab logout functionality is preserved +- WebSocket connections may need reconnection after session changes +- All requests now have consistent security behavior diff --git a/autogpt_platform/frontend/src/lib/supabase/actions.ts b/autogpt_platform/frontend/src/lib/supabase/actions.ts new file mode 100644 index 0000000000..2cf49a2447 --- /dev/null +++ b/autogpt_platform/frontend/src/lib/supabase/actions.ts @@ -0,0 +1,223 @@ +"use server"; +import * as Sentry from "@sentry/nextjs"; +import type { User } from "@supabase/supabase-js"; +import { revalidatePath } from "next/cache"; +import { redirect } from "next/navigation"; +import { getRedirectPath } from "./helpers"; +import { getServerSupabase } from "./server/getServerSupabase"; + +export interface SessionValidationResult { + user: User | null; + isValid: boolean; + redirectPath?: string; +} + +export async function validateSession( + currentPath: string, +): Promise { + return await Sentry.withServerActionInstrumentation( + "validateSession", + {}, + async () => { + const supabase = await getServerSupabase(); + + if (!supabase) { + return { + user: null, + isValid: false, + redirectPath: getRedirectPath(currentPath) || undefined, + }; + } + + try { + const { + data: { user }, + error, + } = await supabase.auth.getUser(); + + if (error || !user) { + const redirectPath = getRedirectPath(currentPath); + return { + user: null, + isValid: false, + redirectPath: redirectPath || undefined, + }; + } + + return { + user, + isValid: true, + }; + } catch (error) { + console.error("Session validation error:", error); + const redirectPath = getRedirectPath(currentPath); + return { + user: null, + isValid: false, + redirectPath: redirectPath || undefined, + }; + } + }, + ); +} + +export async function getCurrentUser(): Promise<{ + user: User | null; + error?: string; +}> { + return await Sentry.withServerActionInstrumentation( + "getCurrentUser", + {}, + async () => { + const supabase = await getServerSupabase(); + + if (!supabase) { + return { + user: null, + error: "Supabase client not available", + }; + } + + try { + const { + data: { user }, + error, + } = await supabase.auth.getUser(); + + if (error) { + return { + user: null, + error: error.message, + }; + } + + return { user }; + } catch (error) { + console.error("Get current user error:", error); + return { + user: null, + error: error instanceof Error ? error.message : "Unknown error", + }; + } + }, + ); +} + +export async function getWebSocketToken(): Promise<{ + token: string | null; + error?: string; +}> { + return await Sentry.withServerActionInstrumentation( + "getWebSocketToken", + {}, + async () => { + const supabase = await getServerSupabase(); + + if (!supabase) { + return { + token: null, + error: "Supabase client not available", + }; + } + + try { + const { + data: { session }, + error, + } = await supabase.auth.getSession(); + + if (error) { + return { + token: null, + error: error.message, + }; + } + + return { token: session?.access_token || null }; + } catch (error) { + console.error("Get WebSocket token error:", error); + return { + token: null, + error: error instanceof Error ? error.message : "Unknown error", + }; + } + }, + ); +} + +export type ServerLogoutOptions = { + globalLogout?: boolean; +}; + +export async function serverLogout(options: ServerLogoutOptions = {}) { + return await Sentry.withServerActionInstrumentation( + "serverLogout", + {}, + async () => { + const supabase = await getServerSupabase(); + + if (!supabase) { + redirect("/login"); + return; + } + + try { + const { error } = await supabase.auth.signOut({ + scope: options.globalLogout ? "global" : "local", + }); + + if (error) { + console.error("Error logging out:", error); + } + } catch (error) { + console.error("Logout error:", error); + } + + // Clear all cached data and redirect + revalidatePath("/", "layout"); + redirect("/login"); + }, + ); +} + +export async function refreshSession() { + return await Sentry.withServerActionInstrumentation( + "refreshSession", + {}, + async () => { + const supabase = await getServerSupabase(); + + if (!supabase) { + return { + user: null, + error: "Supabase client not available", + }; + } + + try { + const { + data: { user }, + error, + } = await supabase.auth.refreshSession(); + + if (error) { + return { + user: null, + error: error.message, + }; + } + + // Revalidate the layout to update server components + revalidatePath("/", "layout"); + + return { user }; + } catch (error) { + console.error("Refresh session error:", error); + return { + user: null, + error: error instanceof Error ? error.message : "Unknown error", + }; + } + }, + ); +} diff --git a/autogpt_platform/frontend/src/lib/supabase/getSupabaseClient.ts b/autogpt_platform/frontend/src/lib/supabase/getSupabaseClient.ts deleted file mode 100644 index 055e31f394..0000000000 --- a/autogpt_platform/frontend/src/lib/supabase/getSupabaseClient.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { getServerSupabase } from "@/lib/supabase/server/getServerSupabase"; -import { createBrowserClient } from "@supabase/ssr"; - -const isClient = typeof window !== "undefined"; - -export const getSupabaseClient = async () => { - return isClient - ? createBrowserClient( - process.env.NEXT_PUBLIC_SUPABASE_URL!, - process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, - { isSingleton: true }, - ) - : await getServerSupabase(); -}; diff --git a/autogpt_platform/frontend/src/lib/supabase/helpers.ts b/autogpt_platform/frontend/src/lib/supabase/helpers.ts index 4d44c1ab07..2118b0e474 100644 --- a/autogpt_platform/frontend/src/lib/supabase/helpers.ts +++ b/autogpt_platform/frontend/src/lib/supabase/helpers.ts @@ -1,4 +1,7 @@ -// Session management constants and utilities +import { type CookieOptions } from "@supabase/ssr"; + +// Detect if we're in a Playwright test environment +const isTest = process.env.NEXT_PUBLIC_PW_TEST === "true"; export const PROTECTED_PAGES = [ "/monitor", @@ -15,6 +18,20 @@ export const STORAGE_KEYS = { LOGOUT: "supabase-logout", } as const; +export function getCookieSettings(): Partial { + if (isTest) + return { + secure: false, + sameSite: "lax", + }; + + return { + secure: process.env.NODE_ENV === "production", + sameSite: "lax", + httpOnly: true, + } as const; +} + // Page protection utilities export function isProtectedPage(pathname: string): boolean { return PROTECTED_PAGES.some((page) => pathname.startsWith(page)); diff --git a/autogpt_platform/frontend/src/lib/supabase/hooks/useSupabase.ts b/autogpt_platform/frontend/src/lib/supabase/hooks/useSupabase.ts index 55cfab6544..333b535fbd 100644 --- a/autogpt_platform/frontend/src/lib/supabase/hooks/useSupabase.ts +++ b/autogpt_platform/frontend/src/lib/supabase/hooks/useSupabase.ts @@ -1,8 +1,15 @@ "use client"; -import { useEffect, useMemo, useState, useRef } from "react"; import { createBrowserClient } from "@supabase/ssr"; import { User } from "@supabase/supabase-js"; -import { useRouter } from "next/navigation"; +import { usePathname, useRouter } from "next/navigation"; +import { useEffect, useMemo, useRef, useState } from "react"; +import { + getCurrentUser, + refreshSession, + serverLogout, + ServerLogoutOptions, + validateSession, +} from "../actions"; import { broadcastLogout, getRedirectPath, @@ -12,16 +19,23 @@ import { export function useSupabase() { const router = useRouter(); + const pathname = usePathname(); const [user, setUser] = useState(null); const [isUserLoading, setIsUserLoading] = useState(true); const lastValidationRef = useRef(0); + const isValidatingRef = useRef(false); const supabase = useMemo(() => { try { return createBrowserClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, - { isSingleton: true }, + { + isSingleton: true, + auth: { + persistSession: false, // Don't persist session on client with httpOnly cookies + }, + }, ); } catch (error) { console.error("Error creating Supabase client", error); @@ -29,75 +43,92 @@ export function useSupabase() { } }, []); - async function logOut() { - if (!supabase) return; - + async function logOut(options: ServerLogoutOptions = {}) { broadcastLogout(); - const { error } = await supabase.auth.signOut({ - scope: "global", - }); - if (error) console.error("Error logging out:", error); - - router.push("/login"); + try { + await serverLogout(options); + } catch (error) { + console.error("Error logging out:", error); + router.push("/login"); + } } - async function validateSession() { - if (!supabase) return false; + async function validateSessionServer() { + // Prevent concurrent validation calls + if (isValidatingRef.current) return true; // Simple debounce - only validate if 2 seconds have passed const now = Date.now(); if (now - lastValidationRef.current < 2000) { return true; } + + isValidatingRef.current = true; lastValidationRef.current = now; try { - const { - data: { user: apiUser }, - error, - } = await supabase.auth.getUser(); + const result = await validateSession(pathname); - if (error || !apiUser) { - // Session is invalid, clear local state + if (!result.isValid) { setUser(null); - const redirectPath = getRedirectPath(window.location.pathname); - if (redirectPath) { - router.push(redirectPath); + if (result.redirectPath) { + router.push(result.redirectPath); } return false; } - // Update local state if we have a valid user but no local user - if (apiUser && !user) { - setUser(apiUser); + // Update local state with server user data + if (result.user) { + setUser((currentUser) => { + // Only update if user actually changed to prevent unnecessary re-renders + if (currentUser?.id !== result.user?.id) { + return result.user; + } + return currentUser; + }); } return true; } catch (error) { console.error("Session validation error:", error); setUser(null); - const redirectPath = getRedirectPath(window.location.pathname); + const redirectPath = getRedirectPath(pathname); if (redirectPath) { router.push(redirectPath); } return false; + } finally { + isValidatingRef.current = false; + } + } + + async function getUserFromServer() { + try { + const { user: serverUser, error } = await getCurrentUser(); + + if (error || !serverUser) { + setUser(null); + return null; + } + + setUser(serverUser); + return serverUser; + } catch (error) { + console.error("Get user error:", error); + setUser(null); + return null; } } function handleCrossTabLogout(e: StorageEvent) { if (!isLogoutEvent(e)) return; - // Clear the Supabase session first - if (supabase) { - supabase.auth.signOut({ scope: "global" }).catch(console.error); - } - // Clear local state immediately setUser(null); router.refresh(); - const redirectPath = getRedirectPath(window.location.pathname); + const redirectPath = getRedirectPath(pathname); if (redirectPath) { router.push(redirectPath); } @@ -105,36 +136,20 @@ export function useSupabase() { function handleVisibilityChange() { if (document.visibilityState === "visible") { - validateSession(); + validateSessionServer(); } } function handleFocus() { - validateSession(); + validateSessionServer(); } useEffect(() => { - if (!supabase) { - setIsUserLoading(false); - return; - } - - const { - data: { subscription }, - } = supabase.auth.onAuthStateChange((_, session) => { - const newUser = session?.user ?? null; - - // Only update if user actually changed to prevent unnecessary re-renders - setUser((currentUser) => { - if (currentUser?.id !== newUser?.id) { - return newUser; - } - return currentUser; - }); - + getUserFromServer().finally(() => { setIsUserLoading(false); }); + // Set up event listeners for cross-tab logout, focus, and visibility change const eventListeners = setupSessionEventListeners( handleVisibilityChange, handleFocus, @@ -142,17 +157,17 @@ export function useSupabase() { ); return () => { - subscription.unsubscribe(); eventListeners.cleanup(); }; - }, [supabase]); + }, []); return { - supabase, + supabase, // Available for non-auth operations like real-time subscriptions user, isLoggedIn: !isUserLoading ? !!user : null, isUserLoading, logOut, - validateSession, + validateSession: validateSessionServer, + refreshSession, }; } diff --git a/autogpt_platform/frontend/src/lib/supabase/middleware.ts b/autogpt_platform/frontend/src/lib/supabase/middleware.ts index 0710daea71..0d5607abb2 100644 --- a/autogpt_platform/frontend/src/lib/supabase/middleware.ts +++ b/autogpt_platform/frontend/src/lib/supabase/middleware.ts @@ -1,6 +1,6 @@ import { createServerClient } from "@supabase/ssr"; import { NextResponse, type NextRequest } from "next/server"; -import { isAdminPage, isProtectedPage } from "./helpers"; +import { getCookieSettings, isAdminPage, isProtectedPage } from "./helpers"; export async function updateSession(request: NextRequest) { let supabaseResponse = NextResponse.next({ @@ -32,9 +32,12 @@ export async function updateSession(request: NextRequest) { supabaseResponse = NextResponse.next({ request, }); - cookiesToSet.forEach(({ name, value, options }) => - supabaseResponse.cookies.set(name, value, options), - ); + cookiesToSet.forEach(({ name, value, options }) => { + supabaseResponse.cookies.set(name, value, { + ...options, + ...getCookieSettings(), + }); + }); }, }, }, diff --git a/autogpt_platform/frontend/src/lib/supabase/server/getServerSupabase.ts b/autogpt_platform/frontend/src/lib/supabase/server/getServerSupabase.ts index da85a5195a..e0acc6a600 100644 --- a/autogpt_platform/frontend/src/lib/supabase/server/getServerSupabase.ts +++ b/autogpt_platform/frontend/src/lib/supabase/server/getServerSupabase.ts @@ -1,4 +1,5 @@ import { createServerClient, type CookieOptions } from "@supabase/ssr"; +import { getCookieSettings } from "../helpers"; type Cookies = { name: string; value: string; options?: CookieOptions }[]; @@ -19,9 +20,12 @@ export async function getServerSupabase() { }, setAll(cookiesToSet: Cookies) { try { - cookiesToSet.forEach(({ name, value, options }) => - cookieStore.set(name, value, options), - ); + cookiesToSet.forEach(({ name, value, options }) => { + cookieStore.set(name, value, { + ...options, + ...getCookieSettings(), + }); + }); } catch { // The `setAll` method was called from a Server Component. // This can be ignored if you have middleware refreshing diff --git a/autogpt_platform/frontend/src/lib/utils/url.ts b/autogpt_platform/frontend/src/lib/utils/url.ts new file mode 100644 index 0000000000..f8196034d7 --- /dev/null +++ b/autogpt_platform/frontend/src/lib/utils/url.ts @@ -0,0 +1,16 @@ +/** + * Extracts the hostname from a URL string. + * @param url - The URL string to extract the hostname from + * @returns The hostname if valid, null if invalid + */ +export const getHostFromUrl = (url: string): string | null => { + try { + if (!url.startsWith("http://") && !url.startsWith("https://")) { + url = "http://" + url; // Add a scheme if missing for URL parsing + } + const urlObj = new URL(url); + return urlObj.hostname; + } catch { + return null; + } +}; diff --git a/autogpt_platform/frontend/tailwind.config.ts b/autogpt_platform/frontend/tailwind.config.ts index d0607b3412..f154b8931f 100644 --- a/autogpt_platform/frontend/tailwind.config.ts +++ b/autogpt_platform/frontend/tailwind.config.ts @@ -116,11 +116,19 @@ const config = { 96: "24rem", }, borderRadius: { + // Design system border radius tokens from Figma + xs: "0.25rem", // 4px + s: "0.5rem", // 8px + m: "0.75rem", // 12px + l: "1rem", // 16px + xl: "1.25rem", // 20px + "2xl": "1.5rem", // 24px + full: "9999px", // For pill buttons + + // Legacy values - kept for backward compatibility lg: "var(--radius)", md: "calc(var(--radius) - 2px)", sm: "calc(var(--radius) - 4px)", - // Add a full radius for pill-shaped buttons - full: "9999px", }, boxShadow: { subtle: "0px 1px 2px 0px rgba(0,0,0,0.05)", diff --git a/docs/content/platform/contributing/oauth-integration-flow.md b/docs/content/platform/contributing/oauth-integration-flow.md new file mode 100644 index 0000000000..dbc7a54be5 --- /dev/null +++ b/docs/content/platform/contributing/oauth-integration-flow.md @@ -0,0 +1,469 @@ +# OAuth Integration Flow Documentation + +## Overview + +The AutoGPT platform implements OAuth 2.0 in two distinct contexts: + +1. **User Authentication (SSO)**: Handled by Supabase for platform login +2. **API Integration Credentials**: Custom OAuth implementation for third-party service access + +This document focuses on the **API Integration OAuth flow** used for connecting to external services. For the list of supported providers, see `/backend/backend/integrations/providers.py`. For user authentication documentation, see the Supabase auth implementation. + +## Trust Boundaries + +### 1. Frontend Trust Boundary +- **Location**: Browser/Client-side application +- **Components**: + - `CredentialsInput` component (`/frontend/src/components/integrations/credentials-input.tsx`) + - OAuth callback route (`/frontend/src/app/(platform)/auth/integrations/oauth_callback/route.ts`) +- **Trust Level**: Untrusted - user-controlled environment +- **Security Measures**: + - CSRF protection via state tokens + - Popup-based flow to prevent URL exposure + - Message validation for cross-window communication + +### 2. Backend API Trust Boundary +- **Location**: Server-side FastAPI application +- **Components**: + - Integration router (`/backend/backend/server/integrations/router.py`) + - OAuth handlers (`/backend/backend/integrations/oauth/`) + - Credentials store (`/backend/backend/integrations/credentials_store.py`) +- **Trust Level**: Trusted - server-controlled environment +- **Security Measures**: + - JWT-based authentication + - Encrypted credential storage + - Token refresh handling + - Scope validation + +### 3. External Provider Trust Boundary +- **Location**: Third-party OAuth providers +- **Components**: Provider authorization endpoints +- **Trust Level**: Semi-trusted - external services +- **Security Measures**: + - HTTPS-only communication + - Provider-specific security features + - Token revocation support + +## Component Architecture + +### Frontend Components + +#### 1. CredentialsInput Component +- **Purpose**: UI component for credential selection and OAuth initiation +- **Key Functions**: + - Displays available credentials + - Initiates OAuth flow via popup window + - Handles OAuth callback messages + - Manages credential selection state + +#### 2. OAuth Callback Route +- **Path**: `/auth/integrations/oauth_callback` +- **Purpose**: Receives OAuth authorization codes from providers +- **Flow**: + 1. Receives `code` and `state` parameters from provider + 2. Posts message to parent window with results + 3. Auto-closes popup window + +### Backend Components + +#### 1. Integration Router +- **Base Path**: `/api/integrations` +- **Key Endpoints**: + - `GET /{provider}/login` - Initiates OAuth flow + - `POST /{provider}/callback` - Exchanges auth code for tokens + - `GET /credentials` - Lists user credentials + - `DELETE /{provider}/credentials/{id}` - Revokes credentials + +#### 2. OAuth Base Handler +- **Purpose**: Abstract base class for provider-specific OAuth implementations +- **Key Methods**: + - `get_login_url()` - Constructs provider authorization URL + - `exchange_code_for_tokens()` - Exchanges auth code for access tokens + - `refresh_tokens()` - Refreshes expired access tokens + - `revoke_tokens()` - Revokes tokens at provider + +#### 3. Credentials Store +- **Purpose**: Manages credential persistence and state +- **Key Features**: + - Redis-backed mutex for concurrent access control + - OAuth state token generation and validation + - PKCE support with code challenge generation + - Default system credentials injection + +## OAuth Flow Sequence + +### 1. Flow Initiation +```mermaid +sequenceDiagram + participant User + participant Frontend + participant Backend + participant Redis + participant Provider + + User->>Frontend: Click "Sign in with Provider" + Frontend->>Backend: GET /api/integrations/{provider}/login + Backend->>Redis: Store state token + code verifier + Backend->>Frontend: Return login URL + state token + Frontend->>Frontend: Open popup window + Frontend->>Provider: Redirect to authorization URL +``` + +### 2. Authorization +```mermaid +sequenceDiagram + participant User + participant Provider + participant Callback + participant Frontend + participant Backend + + User->>Provider: Authorize application + Provider->>Callback: Redirect with code + state + Callback->>Frontend: PostMessage with code + state + Frontend->>Backend: POST /api/integrations/{provider}/callback + Backend->>Provider: Exchange code for tokens + Provider->>Backend: Return access + refresh tokens + Backend->>Backend: Store credentials + Backend->>Frontend: Return credential metadata +``` + +### 3. Token Refresh +```mermaid +sequenceDiagram + participant Application + participant Backend + participant Provider + + Application->>Backend: Request with credential ID + Backend->>Backend: Check token expiry + Backend->>Provider: POST refresh token + Provider->>Backend: Return new tokens + Backend->>Backend: Update stored credentials + Backend->>Application: Return valid access token +``` + +## System Architecture Diagram + +```mermaid +graph TB + subgraph "OAuth Use Cases" + subgraph "User SSO Login" + LP[Login Page] + SB[Supabase Auth] + GO[Google OAuth SSO] + SC[Session Cookies] + end + + subgraph "API Integration OAuth" + UI[CredentialsInput Component] + CB[OAuth Callback Route] + PW[Popup Window] + end + end + + subgraph "Backend API (Trusted)" + subgraph "Auth Management" + SA[Supabase Client] + UM[User Management] + end + + subgraph "Integration Management" + IR[Integration Router] + OH[OAuth Handlers] + CS[Credentials Store] + CM[Credentials Manager] + end + end + + subgraph "Storage" + RD[(Redis)] + PG[(PostgreSQL)] + SDB[(Supabase DB)] + end + + subgraph "External Providers" + GH[GitHub OAuth] + GL[Google APIs OAuth] + NT[Notion OAuth] + OT[...Other Providers] + end + + %% User Login Flow + LP -->|Login with Google| SB + SB -->|OAuth Request| GO + GO -->|User Auth| SB + SB -->|Session| SC + SB -->|User Data| SDB + + %% API Integration Flow + UI -->|1. Initiate OAuth| IR + IR -->|2. Generate State| RD + IR -->|3. Return Auth URL| UI + UI -->|4. Open Popup| PW + PW -->|5. Redirect| GH + GH -->|6. Auth Code| CB + CB -->|7. PostMessage| UI + UI -->|8. Send Code| IR + IR -->|9. Exchange Code| OH + OH -->|10. Get Tokens| GH + OH -->|11. Store Creds| CS + CS -->|12. Save| PG + + OH -.->|Token Refresh| GL + OH -.->|Token Refresh| NT + OH -.->|Token Refresh| OT +``` + +## Data Flow Diagram + +```mermaid +graph LR + subgraph "Data Types" + ST[State Token] + CV[Code Verifier] + CC[Code Challenge] + AC[Auth Code] + AT[Access Token] + RT[Refresh Token] + end + + subgraph "Frontend Flow" + U1[User Initiates] + U2[Receives State] + U3[Opens Popup] + U4[Receives Code] + U5[Sends to Backend] + end + + subgraph "Backend Flow" + B1[Generate State] + B2[Store in Redis] + B3[Validate State] + B4[Exchange Code] + B5[Store Credentials] + end + + U1 --> B1 + B1 --> ST + B1 --> CV + CV --> CC + B2 --> U2 + U3 --> AC + AC --> U4 + U5 --> B3 + B3 --> B4 + B4 --> AT + B4 --> RT + AT --> B5 + RT --> B5 +``` + +## Security Architecture + +```mermaid +graph TB + subgraph "Security Layers" + subgraph "Transport Security" + HTTPS[HTTPS Only] + CSP[Content Security Policy] + end + + subgraph "Authentication" + JWT[JWT Tokens] + STATE[CSRF State Tokens] + PKCE[PKCE Challenge] + end + + subgraph "Storage Security" + ENC[Encrypted Credentials] + SEC[SecretStr Type] + MUTEX[Redis Mutex Locks] + end + + subgraph "Access Control" + USER[User Scoped] + SCOPE[OAuth Scopes] + EXPIRE[Token Expiration] + end + end + + HTTPS --> JWT + JWT --> USER + STATE --> PKCE + PKCE --> ENC + ENC --> SEC + SEC --> MUTEX + USER --> SCOPE + SCOPE --> EXPIRE +``` + +## Credential Lifecycle + +```mermaid +stateDiagram-v2 + [*] --> Initiated: User clicks sign-in + Initiated --> Authorizing: Popup opened + Authorizing --> Authorized: User approves + Authorizing --> Failed: User denies + Authorized --> Active: Tokens stored + Active --> Refreshing: Token expires + Refreshing --> Active: Token refreshed + Refreshing --> Expired: Refresh fails + Active --> Revoked: User deletes + Failed --> [*] + Expired --> [*] + Revoked --> [*] + + note right of Active: Credentials can be used + note right of Refreshing: Automatic process + note right of Revoked: Tokens revoked at provider +``` + +## OAuth Types Comparison + +### User Authentication (SSO) via Supabase + +- **Purpose**: Authenticate users to access the AutoGPT platform +- **Provider**: Supabase Auth (currently supports Google SSO) +- **Flow Path**: `/login` → Supabase OAuth → `/auth/callback` +- **Session Storage**: Supabase-managed cookies +- **Token Management**: Automatic by Supabase +- **User Experience**: Single sign-on to the platform + +### API Integration Credentials + +- **Purpose**: Grant AutoGPT access to user's third-party services +- **Providers**: Examples include GitHub, Google APIs, Notion, and others + - Full list in `/backend/backend/integrations/providers.py` + - OAuth handlers in `/backend/backend/integrations/oauth/` +- **Flow Path**: Integration settings → `/api/integrations/{provider}/login` → `/auth/integrations/oauth_callback` +- **Credential Storage**: Encrypted in PostgreSQL +- **Token Management**: Custom refresh logic with mutex locking +- **User Experience**: Connect external services to use in workflows + +## Data Flow and Security + +### 1. State Token Flow + +- **Generation**: Random 32-byte token using `secrets.token_urlsafe()` +- **Storage**: Redis with 10-minute expiration +- **Validation**: Constant-time comparison using `secrets.compare_digest()` +- **Purpose**: CSRF protection and request correlation + +### 2. PKCE Implementation + +- **Code Verifier**: Random string generated using `secrets.token_urlsafe(128)` (approximately 171 characters when base64url encoded, though RFC 7636 recommends 43-128 characters) +- **Code Challenge**: SHA256 hash of verifier, base64url encoded +- **Storage**: Stored with state token in database (encrypted) with 10-minute expiration +- **Usage**: Enhanced security for public clients (currently used by Twitter provider) + +### 3. Credential Storage + +- **Structure**: + + ```python + OAuth2Credentials: + - id: UUID + - provider: ProviderName + - access_token: SecretStr (encrypted) + - refresh_token: Optional[SecretStr] + - scopes: List[str] + - expires_at: Optional[int] + - username: Optional[str] + ``` + +- **Persistence**: PostgreSQL via Prisma ORM +- **Access Control**: User-scoped with mutex locking + +### 4. Token Security + +- **Storage**: Tokens stored as `SecretStr` type +- **Transport**: HTTPS-only, never logged +- **Refresh**: Automatic refresh 5 minutes before expiry +- **Revocation**: Supported for providers that implement it + +## Provider Implementations + +### Supported Providers + +The platform supports various OAuth providers including GitHub, Google, Notion, Twitter, and others. For the complete list, see: +- `/backend/backend/integrations/providers.py` - All supported providers +- `/backend/backend/integrations/oauth/` - OAuth implementations + +### Provider-Specific Security Considerations + +- **GitHub**: Supports optional token expiration - tokens may be non-expiring by default +- **Linear**: Returns scopes as space-separated string, requiring special parsing +- **Google**: Requires explicit offline access scope for refresh tokens +- **Twitter**: Uses PKCE for enhanced security on public clients + +Each provider handler implements the security measures defined in `BaseOAuthHandler`, ensuring consistent token management and refresh logic across all integrations. + +## Security Best Practices + +### 1. Frontend Security + +- Use popup windows to prevent URL tampering +- Validate state tokens before processing callbacks +- Clear sensitive data from window messages +- Implement timeout for OAuth flows (5 minutes) + +### 2. Backend Security + +- Store client secrets in environment variables +- Use HTTPS for all OAuth endpoints +- Implement proper scope validation +- Log security events without exposing tokens +- Use database transactions for credential updates + +### 3. Token Management + +- Refresh tokens proactively (5 minutes before expiry) +- Revoke tokens when credentials are deleted +- Never expose tokens in logs or error messages +- Use constant-time comparison for token validation + +## Error Handling + +### Common Error Scenarios + +1. **Invalid State Token**: 400 Bad Request +2. **Provider Configuration Missing**: 501 Not Implemented +3. **Token Exchange Failure**: 400 Bad Request with hint +4. **Webhook Conflicts**: 409 Conflict, requires confirmation +5. **Credential Not Found**: 404 Not Found + +### Error Response Format + +```json +{ + "detail": { + "message": "Human-readable error description", + "hint": "Actionable suggestion for resolution" + } +} +``` + +## Testing Considerations + +### Unit Testing + +- Mock OAuth providers for flow testing +- Test state token generation and validation +- Verify PKCE implementation +- Test concurrent access scenarios + +### Integration Testing + +- Use provider sandboxes when available +- Test full OAuth flow with real providers +- Verify token refresh mechanisms +- Test error scenarios and recovery + +### Logging Guidelines + +- Log flow initiation and completion +- Log errors with context (no tokens) +- Track provider-specific issues +- Monitor for suspicious patterns diff --git a/docs/mkdocs.yml b/docs/mkdocs.yml index d670ffccb9..adcbb213cd 100644 --- a/docs/mkdocs.yml +++ b/docs/mkdocs.yml @@ -23,6 +23,7 @@ nav: - Blocks: platform/blocks/blocks.md - Contributing: - Tests: platform/contributing/tests.md + - OAuth Flows: platform/contributing/oauth-integration-flow.md - AutoGPT Classic: - Introduction: classic/index.md - Setup: