File size: 6,369 Bytes
a51a15b |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
import os
from dotenv import load_dotenv
from agentpress.tool import ToolResult, openapi_schema, xml_schema
from sandbox.sandbox import SandboxToolsBase, Sandbox
from utils.files_utils import clean_path
from agentpress.thread_manager import ThreadManager
# Load environment variables
load_dotenv()
class SandboxDeployTool(SandboxToolsBase):
"""Tool for deploying static websites from a Daytona sandbox to Cloudflare Pages."""
def __init__(self, project_id: str, thread_manager: ThreadManager):
super().__init__(project_id, thread_manager)
self.workspace_path = "/workspace" # Ensure we're always operating in /workspace
self.cloudflare_api_token = os.getenv("CLOUDFLARE_API_TOKEN")
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=[
{"param_name": "name", "node_type": "attribute", "path": "name"},
{"param_name": "directory_path", "node_type": "attribute", "path": "directory_path"}
],
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
NOTE: If the same name is used, it will redeploy to the same project as before
-->
<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:
# Ensure sandbox is initialized
await self._ensure_sandbox()
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")
# Single command that creates the project if it doesn't exist and then deploys
project_name = f"{self.sandbox_id}-{name}"
deploy_cmd = f'''cd {self.workspace_path} && 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 the command directly using the sandbox's process.exec method
response = self.sandbox.process.exec(deploy_cmd, timeout=300)
print(f"Deployment command output: {response.result}")
if response.exit_code == 0:
return self.success_response({
"message": f"Website deployed successfully",
"output": response.result
})
else:
return self.fail_response(f"Deployment failed with exit code {response.exit_code}: {response.result}")
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)}")
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())
|