fleet-python 0.1.0__py3-none-any.whl → 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of fleet-python might be problematic. Click here for more details.

fleet/env/models.py ADDED
@@ -0,0 +1,127 @@
1
+ # generated by datamodel-codegen:
2
+ # filename: openapi_2.json
3
+ # timestamp: 2025-07-08T20:02:11+00:00
4
+
5
+ from __future__ import annotations
6
+
7
+ from enum import Enum
8
+ from typing import Any, Dict, List, Optional, Union
9
+
10
+ from pydantic import BaseModel, Field
11
+
12
+
13
+ class BrowserDescribeResponse(BaseModel):
14
+ success: bool = Field(..., title="Success")
15
+ url: str = Field(..., title="Url")
16
+ devtools_url: str = Field(..., title="Devtools Url")
17
+
18
+
19
+ class BrowserStartRequest(BaseModel):
20
+ start_page: Optional[str] = Field("about:blank", title="Start Page")
21
+ resolution: Optional[str] = Field("1920x1080", title="Resolution")
22
+
23
+
24
+ class BrowserStartResponse(BaseModel):
25
+ success: bool = Field(..., title="Success")
26
+ message: str = Field(..., title="Message")
27
+
28
+
29
+ class CreateSnapshotsResponse(BaseModel):
30
+ success: bool = Field(..., title="Success")
31
+ initial_snapshot_path: Optional[str] = Field(None, title="Initial Snapshot Path")
32
+ final_snapshot_path: Optional[str] = Field(None, title="Final Snapshot Path")
33
+ message: str = Field(..., title="Message")
34
+
35
+
36
+ class HealthResponse(BaseModel):
37
+ status: str = Field(..., title="Status")
38
+ timestamp: str = Field(..., title="Timestamp")
39
+ service: str = Field(..., title="Service")
40
+
41
+
42
+ class LogActionRequest(BaseModel):
43
+ action_type: str = Field(..., title="Action Type")
44
+ sql: Optional[str] = Field(None, title="Sql")
45
+ args: Optional[str] = Field(None, title="Args")
46
+ path: Optional[str] = Field(None, title="Path")
47
+ raw_payload: Optional[str] = Field(None, title="Raw Payload")
48
+
49
+
50
+ class LogActionResponse(BaseModel):
51
+ success: bool = Field(..., title="Success")
52
+ message: str = Field(..., title="Message")
53
+
54
+
55
+ class QueryRequest(BaseModel):
56
+ query: str = Field(..., title="Query")
57
+ args: Optional[List] = Field(None, title="Args")
58
+ read_only: Optional[bool] = Field(True, title="Read Only")
59
+
60
+
61
+ class QueryResponse(BaseModel):
62
+ success: bool = Field(..., title="Success")
63
+ columns: Optional[List[str]] = Field(None, title="Columns")
64
+ rows: Optional[List[List]] = Field(None, title="Rows")
65
+ rows_affected: Optional[int] = Field(None, title="Rows Affected")
66
+ last_insert_id: Optional[int] = Field(None, title="Last Insert Id")
67
+ error: Optional[str] = Field(None, title="Error")
68
+ message: str = Field(..., title="Message")
69
+
70
+
71
+ class ResetRequest(BaseModel):
72
+ seed: Optional[int] = Field(None, title="Seed")
73
+ timestamp: Optional[str] = Field(None, title="Timestamp")
74
+
75
+
76
+ class ResetResponse(BaseModel):
77
+ success: bool = Field(..., title="Success")
78
+ message: str = Field(..., title="Message")
79
+
80
+
81
+ class ResourceMode(Enum):
82
+ ro = "ro"
83
+ rw = "rw"
84
+
85
+
86
+ class ResourceType(Enum):
87
+ sqlite = "sqlite"
88
+ cdp = "cdp"
89
+
90
+
91
+ class TableSchema(BaseModel):
92
+ name: str = Field(..., title="Name")
93
+ sql: str = Field(..., title="Sql")
94
+ columns: List[Dict[str, Any]] = Field(..., title="Columns")
95
+
96
+
97
+ class TimestampResponse(BaseModel):
98
+ timestamp: str = Field(..., title="Timestamp")
99
+
100
+
101
+ class ValidationError(BaseModel):
102
+ loc: List[Union[str, int]] = Field(..., title="Location")
103
+ msg: str = Field(..., title="Message")
104
+ type: str = Field(..., title="Error Type")
105
+
106
+
107
+ class DescribeResponse(BaseModel):
108
+ success: bool = Field(..., title="Success")
109
+ resource_name: str = Field(..., title="Resource Name")
110
+ tables: Optional[List[TableSchema]] = Field(None, title="Tables")
111
+ error: Optional[str] = Field(None, title="Error")
112
+ message: str = Field(..., title="Message")
113
+
114
+
115
+ class HTTPValidationError(BaseModel):
116
+ detail: Optional[List[ValidationError]] = Field(None, title="Detail")
117
+
118
+
119
+ class Resource(BaseModel):
120
+ name: str = Field(..., title="Name")
121
+ type: ResourceType
122
+ mode: ResourceMode
123
+ label: Optional[str] = Field(None, title="Label")
124
+
125
+
126
+ class ResourcesResponse(BaseModel):
127
+ resources: List[Resource] = Field(..., title="Resources")
fleet/models.py ADDED
@@ -0,0 +1,109 @@
1
+ # generated by datamodel-codegen:
2
+ # filename: openapi.json
3
+ # timestamp: 2025-07-08T18:21:47+00:00
4
+
5
+ from __future__ import annotations
6
+
7
+ from enum import Enum
8
+ from typing import Dict, List, Optional, Union
9
+
10
+ from pydantic import BaseModel, Field
11
+
12
+
13
+ class Environment(BaseModel):
14
+ env_key: str = Field(..., title="Env Key")
15
+ name: str = Field(..., title="Name")
16
+ description: Optional[str] = Field(..., title="Description")
17
+ default_version: Optional[str] = Field(..., title="Default Version")
18
+ versions: Dict[str, str] = Field(..., title="Versions")
19
+
20
+
21
+ class Instance(BaseModel):
22
+ instance_id: str = Field(..., title="Instance Id")
23
+ env_key: str = Field(..., title="Env Key")
24
+ version: str = Field(..., title="Version")
25
+ status: str = Field(..., title="Status")
26
+ subdomain: str = Field(..., title="Subdomain")
27
+ created_at: str = Field(..., title="Created At")
28
+ updated_at: str = Field(..., title="Updated At")
29
+ terminated_at: Optional[str] = Field(None, title="Terminated At")
30
+ team_id: str = Field(..., title="Team Id")
31
+ region: str = Field(..., title="Region")
32
+
33
+
34
+ class InstanceRequest(BaseModel):
35
+ env_key: str = Field(..., title="Env Key")
36
+ version: Optional[str] = Field(None, title="Version")
37
+ region: Optional[str] = Field("us-east-2", title="Region")
38
+ seed: Optional[int] = Field(None, title="Seed")
39
+ timestamp: Optional[int] = Field(None, title="Timestamp")
40
+ p_error: Optional[float] = Field(None, title="P Error")
41
+ avg_latency: Optional[float] = Field(None, title="Avg Latency")
42
+ run_id: Optional[str] = Field(None, title="Run Id")
43
+ task_id: Optional[str] = Field(None, title="Task Id")
44
+
45
+
46
+ class InstanceStatus(Enum):
47
+ pending = "pending"
48
+ running = "running"
49
+ stopped = "stopped"
50
+ error = "error"
51
+
52
+
53
+ class ManagerURLs(BaseModel):
54
+ api: str = Field(..., title="Api")
55
+ docs: str = Field(..., title="Docs")
56
+ reset: str = Field(..., title="Reset")
57
+ diff: str = Field(..., title="Diff")
58
+ snapshot: str = Field(..., title="Snapshot")
59
+ execute_verifier_function: str = Field(..., title="Execute Verifier Function")
60
+ execute_verifier_function_with_upload: str = Field(
61
+ ..., title="Execute Verifier Function With Upload"
62
+ )
63
+
64
+
65
+ class ValidationError(BaseModel):
66
+ loc: List[Union[str, int]] = Field(..., title="Location")
67
+ msg: str = Field(..., title="Message")
68
+ type: str = Field(..., title="Error Type")
69
+
70
+
71
+ class HTTPValidationError(BaseModel):
72
+ detail: Optional[List[ValidationError]] = Field(None, title="Detail")
73
+
74
+
75
+ class InstanceURLs(BaseModel):
76
+ root: str = Field(..., title="Root")
77
+ app: str = Field(..., title="App")
78
+ api: Optional[str] = Field(None, title="Api")
79
+ health: Optional[str] = Field(None, title="Health")
80
+ api_docs: Optional[str] = Field(None, title="Api Docs")
81
+ manager: ManagerURLs
82
+
83
+
84
+ class InstanceResponse(BaseModel):
85
+ instance_id: str = Field(..., title="Instance Id")
86
+ env_key: str = Field(..., title="Env Key")
87
+ version: str = Field(..., title="Version")
88
+ status: str = Field(..., title="Status")
89
+ subdomain: str = Field(..., title="Subdomain")
90
+ created_at: str = Field(..., title="Created At")
91
+ updated_at: str = Field(..., title="Updated At")
92
+ terminated_at: Optional[str] = Field(None, title="Terminated At")
93
+ team_id: str = Field(..., title="Team Id")
94
+ region: str = Field(..., title="Region")
95
+ urls: InstanceURLs
96
+ health: Optional[bool] = Field(None, title="Health")
97
+
98
+
99
+ class InstanceRecord(BaseModel):
100
+ instance_id: str
101
+ env_key: str
102
+ version: str
103
+ status: str
104
+ subdomain: str
105
+ created_at: str
106
+ updated_at: str
107
+ terminated_at: Optional[str] = None
108
+ team_id: str
109
+ region: str
@@ -0,0 +1,23 @@
1
+ from abc import ABC
2
+ from ..env.models import Resource as ResourceModel, ResourceType, ResourceMode
3
+
4
+
5
+ class Resource(ABC):
6
+ def __init__(self, resource: ResourceModel):
7
+ self.resource = resource
8
+
9
+ @property
10
+ def uri(self) -> str:
11
+ return f"{self.resource.type}://{self.resource.name}"
12
+
13
+ @property
14
+ def name(self) -> str:
15
+ return self.resource.name
16
+
17
+ @property
18
+ def type(self) -> ResourceType:
19
+ return self.resource.type
20
+
21
+ @property
22
+ def mode(self) -> ResourceMode:
23
+ return self.resource.mode
@@ -0,0 +1,18 @@
1
+ from ..env.models import Resource as ResourceModel
2
+ from ..env.models import BrowserDescribeResponse
3
+ from .base import Resource
4
+
5
+ from typing import TYPE_CHECKING
6
+
7
+ if TYPE_CHECKING:
8
+ from ..env.base import AsyncWrapper
9
+
10
+
11
+ class AsyncBrowserResource(Resource):
12
+ def __init__(self, resource: ResourceModel, client: "AsyncWrapper"):
13
+ super().__init__(resource)
14
+ self.client = client
15
+
16
+ async def describe(self) -> BrowserDescribeResponse:
17
+ response = await self.client.request("GET", "/resource/cdp/describe")
18
+ return BrowserDescribeResponse(**response.json())
@@ -0,0 +1,41 @@
1
+ from typing import Any, List, Optional
2
+ from ..env.models import Resource as ResourceModel
3
+ from ..env.models import DescribeResponse, QueryRequest, QueryResponse
4
+ from .base import Resource
5
+
6
+ from typing import TYPE_CHECKING
7
+
8
+ if TYPE_CHECKING:
9
+ from ..env.base import AsyncWrapper
10
+
11
+
12
+ class AsyncSQLiteResource(Resource):
13
+ def __init__(self, resource: ResourceModel, client: "AsyncWrapper"):
14
+ super().__init__(resource)
15
+ self.client = client
16
+
17
+ async def describe(self) -> DescribeResponse:
18
+ """Describe the SQLite database schema."""
19
+ response = await self.client.request(
20
+ "GET", f"/resource/sqlite/{self.resource.name}/describe"
21
+ )
22
+ return DescribeResponse(**response.json())
23
+
24
+ async def query(
25
+ self, query: str, args: Optional[List[Any]] = None
26
+ ) -> QueryResponse:
27
+ return await self._query(query, args, read_only=True)
28
+
29
+ async def exec(self, query: str, args: Optional[List[Any]] = None) -> QueryResponse:
30
+ return await self._query(query, args, read_only=False)
31
+
32
+ async def _query(
33
+ self, query: str, args: Optional[List[Any]] = None, read_only: bool = True
34
+ ) -> QueryResponse:
35
+ request = QueryRequest(query=query, args=args, read_only=read_only)
36
+ response = await self.client.request(
37
+ "POST",
38
+ f"/resource/sqlite/{self.resource.name}/query",
39
+ json=request.model_dump(),
40
+ )
41
+ return QueryResponse(**response.json())
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: fleet-python
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: Python SDK for Fleet environments
5
5
  Author-email: Fleet AI <nic@fleet.so>
6
6
  License: Apache-2.0
@@ -22,6 +22,7 @@ Description-Content-Type: text/markdown
22
22
  License-File: LICENSE
23
23
  Requires-Dist: aiohttp>=3.8.0
24
24
  Requires-Dist: pydantic>=2.0.0
25
+ Requires-Dist: httpx>=0.27.0
25
26
  Requires-Dist: typing-extensions>=4.0.0
26
27
  Provides-Extra: dev
27
28
  Requires-Dist: pytest>=7.0.0; extra == "dev"
@@ -0,0 +1,19 @@
1
+ examples/browser_control_example.py,sha256=2nEzUc83bliqR9jYTenkrLTGoKSkWahjP4WpiaHIMxw,1520
2
+ examples/quickstart.py,sha256=AnLlLQYRfrP7y2J69d1HZRlZsjJT-KeBu897MoNfqYM,4671
3
+ fleet/__init__.py,sha256=F8esCp6DcdAc4JAhWlCd_G3pBKRBzuLuVZnElWv5GGQ,987
4
+ fleet/base.py,sha256=lgKhPZhLotP4Iqn7Z4nQTfCvuD3bT37MoucTrSRE2zs,2006
5
+ fleet/client.py,sha256=xMyKc49YwTAh1F5Wbk5hdaB4F6wlrl41a3EjyBdjFUk,5660
6
+ fleet/exceptions.py,sha256=yG3QWprCw1OnF-vdFBFJWE4m3ftBLBng31Dr__VbjI4,2249
7
+ fleet/models.py,sha256=Jf6Zmk689TPXhTSnVENK_VCw0VsujWzEWsN3T29MQ0k,3713
8
+ fleet/env/__init__.py,sha256=ScTHui5BAFTTKKZ6TxS71D5r-JYMxxFEkcT_AcAXDsM,145
9
+ fleet/env/base.py,sha256=bm6BGd81TnTDqE_qG6S50Xhikf9DwNqEEk1uKFY7dEk,1540
10
+ fleet/env/client.py,sha256=yVS4cblGlARWeoZC69Uz_KR2wlmYC19d-BsQalLB9kw,8043
11
+ fleet/env/models.py,sha256=mxRoQEzswMEjwdEN-gn2ylG9Z8IZH-Gqs6hVG0fkSVs,3883
12
+ fleet/resources/base.py,sha256=rzFXBoFqtkM0HZpwqN9NHiwQbmeKOGOYh7G4REaHKGc,553
13
+ fleet/resources/browser.py,sha256=mC1g7xpyvEK8vDGXNn0MXGc8TGE83PIW3KOOj9i3rIA,591
14
+ fleet/resources/sqlite.py,sha256=hZA2qcd7SqjsACJEkKzXQwlJCtvNZ9yom2HCjKy7lOs,1484
15
+ fleet_python-0.2.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
16
+ fleet_python-0.2.0.dist-info/METADATA,sha256=qrpy0oFm5wfUvW7VVqbrdZX566B-CB1iJSp4spH9gmg,3069
17
+ fleet_python-0.2.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
18
+ fleet_python-0.2.0.dist-info/top_level.txt,sha256=AOyXOrBXUjPcH4BumElz_D95kiWKNIpUbUPFP_9gCLk,15
19
+ fleet_python-0.2.0.dist-info/RECORD,,
fleet/config.py DELETED
@@ -1,125 +0,0 @@
1
- """Fleet SDK Configuration Management."""
2
-
3
- import os
4
- import logging
5
- from typing import Optional, Dict, Any
6
- from pydantic import BaseModel, Field, validator
7
- from .exceptions import FleetAuthenticationError, FleetConfigurationError
8
-
9
-
10
- logger = logging.getLogger(__name__)
11
-
12
-
13
- class FleetConfig(BaseModel):
14
- """Fleet SDK Configuration."""
15
-
16
- api_key: Optional[str] = Field(None, description="Fleet API key")
17
- base_url: str = Field(default="https://fleet.new", description="Fleet API base URL (hardcoded)")
18
-
19
- @validator('api_key')
20
- def validate_api_key(cls, v):
21
- """Validate API key format."""
22
- if v is not None and not _is_valid_api_key(v):
23
- raise FleetAuthenticationError(
24
- "Invalid API key format. Fleet API keys should start with 'sk_' followed by alphanumeric characters."
25
- )
26
- return v
27
-
28
- @validator('base_url')
29
- def validate_base_url(cls, v):
30
- """Validate base URL format."""
31
- if not v.startswith(('http://', 'https://')):
32
- raise FleetConfigurationError("Base URL must start with 'http://' or 'https://'")
33
- return v.rstrip('/')
34
-
35
- def mask_sensitive_data(self) -> Dict[str, Any]:
36
- """Return config dict with sensitive data masked."""
37
- data = self.dict()
38
- if data.get('api_key'):
39
- data['api_key'] = _mask_api_key(data['api_key'])
40
- return data
41
-
42
- class Config:
43
- """Pydantic configuration."""
44
- extra = 'allow'
45
-
46
-
47
- def get_config(**kwargs: Any) -> FleetConfig:
48
- """Get Fleet configuration from environment variables.
49
-
50
- Loads FLEET_API_KEY from environment variables. The base URL is hardcoded to https://fleet.new.
51
-
52
- Args:
53
- **kwargs: Override specific configuration values
54
-
55
- Returns:
56
- FleetConfig instance
57
-
58
- Raises:
59
- FleetAuthenticationError: If API key is invalid
60
- FleetConfigurationError: If configuration is invalid
61
- """
62
- # Load from environment variables
63
- config_data = _load_env_config()
64
-
65
- # Apply any overrides
66
- config_data.update(kwargs)
67
-
68
- # Create and validate configuration
69
- try:
70
- config = FleetConfig(**config_data)
71
- return config
72
-
73
- except Exception as e:
74
- if isinstance(e, (FleetAuthenticationError, FleetConfigurationError)):
75
- raise
76
- raise FleetConfigurationError(f"Invalid configuration: {e}")
77
-
78
-
79
- def _load_env_config() -> Dict[str, Any]:
80
- """Load configuration from environment variables."""
81
- env_mapping = {
82
- 'FLEET_API_KEY': 'api_key',
83
- # base_url is hardcoded, not configurable via env var
84
- }
85
-
86
- config = {}
87
- for env_var, config_key in env_mapping.items():
88
- value = os.getenv(env_var)
89
- if value is not None:
90
- config[config_key] = value
91
-
92
- return config
93
-
94
-
95
- def _is_valid_api_key(api_key: str) -> bool:
96
- """Validate API key format."""
97
- if not api_key:
98
- return False
99
-
100
- # Fleet API keys start with 'sk_' followed by alphanumeric characters
101
- # This is a basic format check - actual validation happens on the server
102
- if not api_key.startswith('sk_'):
103
- return False
104
-
105
- # Check if the rest contains only alphanumeric characters and underscores
106
- key_part = api_key[3:] # Remove 'sk_' prefix
107
- if not key_part or not key_part.replace('_', '').isalnum():
108
- return False
109
-
110
- # Minimum length check
111
- if len(api_key) < 20:
112
- return False
113
-
114
- return True
115
-
116
-
117
- def _mask_api_key(api_key: str) -> str:
118
- """Mask API key for logging."""
119
- if not api_key:
120
- return api_key
121
-
122
- if len(api_key) < 8:
123
- return '*' * len(api_key)
124
-
125
- return api_key[:4] + '*' * (len(api_key) - 8) + api_key[-4:]