2025-04-13 05:40:01 +08:00
import os
from dotenv import load_dotenv
from agentpress . tool import ToolResult , openapi_schema , xml_schema
2025-04-12 08:04:40 +08:00
from sandbox . sandbox import SandboxToolsBase , Sandbox
2025-04-13 05:40:01 +08:00
from utils . files_utils import clean_path
2025-04-13 06:20:38 +08:00
from agent . tools . sb_shell_tool import SandboxShellTool
2025-04-13 05:40:01 +08:00
# Load environment variables
load_dotenv ( )
class SandboxDeployTool ( SandboxToolsBase ) :
""" Tool for deploying static websites from a Daytona sandbox to Cloudflare Pages. """
2025-04-12 08:04:40 +08:00
def __init__ ( self , sandbox : Sandbox ) :
super ( ) . __init__ ( sandbox )
2025-04-13 05:40:01 +08:00
self . workspace_path = " /workspace " # Ensure we're always operating in /workspace
self . cloudflare_api_token = os . getenv ( " CLOUDFLARE_API_TOKEN " )
2025-04-12 08:04:40 +08:00
self . shell_tool = SandboxShellTool ( sandbox )
2025-04-13 05:40:01 +08:00
def clean_path ( self , path : str ) - > str :
""" Clean and normalize a path to be relative to /workspace """
return clean_path ( path , self . workspace_path )
@openapi_schema ( {
" type " : " function " ,
" function " : {
" name " : " deploy " ,
" description " : " Deploy a static website (HTML+CSS+JS) from a directory in the sandbox to Cloudflare Pages. Only use this tool when permanent deployment to a production environment is needed. The directory path must be relative to /workspace. The website will be deployed to {name} .kortix.cloud. " ,
" parameters " : {
" type " : " object " ,
" properties " : {
" name " : {
" type " : " string " ,
" description " : " Name for the deployment, will be used in the URL as {name} .kortix.cloud "
} ,
" directory_path " : {
" type " : " string " ,
" description " : " Path to the directory containing the static website files to deploy, relative to /workspace (e.g., ' build ' ) "
}
} ,
" required " : [ " name " , " directory_path " ]
}
}
} )
@xml_schema (
tag_name = " deploy " ,
mappings = [
2025-04-13 06:20:38 +08:00
{ " param_name " : " name " , " node_type " : " attribute " , " path " : " name " } ,
{ " param_name " : " directory_path " , " node_type " : " attribute " , " path " : " directory_path " }
2025-04-13 05:40:01 +08:00
] ,
example = '''
< ! - -
IMPORTANT : Only use this tool when :
1. The user explicitly requests permanent deployment to production
2. You have a complete , ready - to - deploy directory
2025-04-13 06:20:38 +08:00
NOTE : If the same name is used , it will redeploy to the same project as before
2025-04-13 05:40:01 +08:00
- - >
< deploy name = " my-site " directory_path = " website " >
< / deploy >
'''
)
async def deploy ( self , name : str , directory_path : str ) - > ToolResult :
"""
Deploy a static website ( HTML + CSS + JS ) from the sandbox to Cloudflare Pages .
Only use this tool when permanent deployment to a production environment is needed .
Args :
name : Name for the deployment , will be used in the URL as { name } . kortix . cloud
directory_path : Path to the directory to deploy , relative to / workspace
Returns :
ToolResult containing :
- Success : Deployment information including URL
- Failure : Error message if deployment fails
"""
try :
directory_path = self . clean_path ( directory_path )
full_path = f " { self . workspace_path } / { directory_path } "
# Verify the directory exists
try :
dir_info = self . sandbox . fs . get_file_info ( full_path )
if not dir_info . is_dir :
return self . fail_response ( f " ' { directory_path } ' is not a directory " )
except Exception as e :
return self . fail_response ( f " Directory ' { directory_path } ' does not exist: { str ( e ) } " )
# Deploy to Cloudflare Pages directly from the container
try :
# Get Cloudflare API token from environment
if not self . cloudflare_api_token :
return self . fail_response ( " CLOUDFLARE_API_TOKEN environment variable not set " )
2025-04-13 06:20:38 +08:00
# Single command that creates the project if it doesn't exist and then deploys
project_name = f " { self . sandbox_id } - { name } "
deploy_cmd = f ''' export CLOUDFLARE_API_TOKEN= { self . cloudflare_api_token } &&
( npx wrangler pages deploy { full_path } - - project - name { project_name } | |
( npx wrangler pages project create { project_name } - - production - branch production & &
npx wrangler pages deploy { full_path } - - project - name { project_name } ) ) '''
# Execute command using shell_tool.execute_command
response = await self . shell_tool . execute_command (
command = deploy_cmd ,
folder = None , # Use the workspace root
timeout = 300 # Increased timeout for deployments
2025-04-13 05:40:01 +08:00
)
2025-04-13 06:20:38 +08:00
print ( f " Deployment response: { response } " )
2025-04-13 05:40:01 +08:00
2025-04-13 06:20:38 +08:00
if response . success :
2025-04-13 05:40:01 +08:00
return self . success_response ( {
" message " : f " Website deployed successfully " ,
2025-04-13 06:20:38 +08:00
" output " : response . output
2025-04-13 05:40:01 +08:00
} )
else :
2025-04-13 06:20:38 +08:00
return self . fail_response ( f " Deployment failed: { response . output } " )
2025-04-13 05:40:01 +08:00
except Exception as e :
return self . fail_response ( f " Error during deployment: { str ( e ) } " )
except Exception as e :
return self . fail_response ( f " Error deploying website: { str ( e ) } " )
2025-04-13 06:20:38 +08:00
if __name__ == " __main__ " :
import asyncio
import sys
async def test_deploy ( ) :
# Replace these with actual values for testing
sandbox_id = " sandbox-ccb30b35 "
password = " test-password "
# Initialize the deploy tool
deploy_tool = SandboxDeployTool ( sandbox_id , password )
# Test deployment - replace with actual directory path and site name
result = await deploy_tool . deploy (
name = " test-site-1x " ,
directory_path = " website " # Directory containing static site files
)
print ( f " Deployment result: { result } " )
asyncio . run ( test_deploy ( ) )