fleet-python 0.2.3__py3-none-any.whl → 0.2.5__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.

Files changed (42) hide show
  1. examples/dsl_example.py +2 -1
  2. examples/example.py +2 -2
  3. examples/json_tasks_example.py +1 -1
  4. examples/nova_act_example.py +1 -1
  5. examples/openai_example.py +17 -24
  6. examples/openai_simple_example.py +11 -12
  7. fleet/__init__.py +18 -3
  8. fleet/_async/base.py +51 -0
  9. fleet/_async/client.py +133 -0
  10. fleet/_async/env/__init__.py +0 -0
  11. fleet/_async/env/client.py +15 -0
  12. fleet/_async/exceptions.py +73 -0
  13. fleet/_async/instance/__init__.py +24 -0
  14. fleet/_async/instance/base.py +37 -0
  15. fleet/_async/instance/client.py +278 -0
  16. fleet/_async/instance/models.py +141 -0
  17. fleet/_async/models.py +109 -0
  18. fleet/_async/playwright.py +291 -0
  19. fleet/_async/resources/__init__.py +0 -0
  20. fleet/_async/resources/base.py +26 -0
  21. fleet/_async/resources/browser.py +41 -0
  22. fleet/_async/resources/sqlite.py +41 -0
  23. fleet/base.py +1 -24
  24. fleet/client.py +31 -99
  25. fleet/env/__init__.py +13 -1
  26. fleet/env/client.py +7 -7
  27. fleet/instance/__init__.py +3 -2
  28. fleet/instance/base.py +1 -24
  29. fleet/instance/client.py +40 -57
  30. fleet/playwright.py +45 -47
  31. fleet/resources/__init__.py +0 -0
  32. fleet/resources/browser.py +14 -14
  33. fleet/resources/sqlite.py +11 -11
  34. fleet/verifiers/__init__.py +5 -10
  35. fleet/verifiers/code.py +1 -132
  36. {fleet_python-0.2.3.dist-info → fleet_python-0.2.5.dist-info}/METADATA +12 -11
  37. fleet_python-0.2.5.dist-info/RECORD +48 -0
  38. {fleet_python-0.2.3.dist-info → fleet_python-0.2.5.dist-info}/top_level.txt +1 -0
  39. scripts/unasync.py +28 -0
  40. fleet_python-0.2.3.dist-info/RECORD +0 -31
  41. {fleet_python-0.2.3.dist-info → fleet_python-0.2.5.dist-info}/WHEEL +0 -0
  42. {fleet_python-0.2.3.dist-info → fleet_python-0.2.5.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,291 @@
1
+ import base64
2
+ from typing import List, Dict, Any
3
+ from playwright.async_api import async_playwright, Browser, Page
4
+ from .client import AsyncEnvironment
5
+
6
+
7
+ # Key mapping for computer use actions
8
+ CUA_KEY_TO_PLAYWRIGHT_KEY = {
9
+ "/": "Divide",
10
+ "\\": "Backslash",
11
+ "alt": "Alt",
12
+ "arrowdown": "ArrowDown",
13
+ "arrowleft": "ArrowLeft",
14
+ "arrowright": "ArrowRight",
15
+ "arrowup": "ArrowUp",
16
+ "backspace": "Backspace",
17
+ "capslock": "CapsLock",
18
+ "cmd": "Meta",
19
+ "ctrl": "Control",
20
+ "delete": "Delete",
21
+ "end": "End",
22
+ "enter": "Enter",
23
+ "esc": "Escape",
24
+ "home": "Home",
25
+ "insert": "Insert",
26
+ "option": "Alt",
27
+ "pagedown": "PageDown",
28
+ "pageup": "PageUp",
29
+ "shift": "Shift",
30
+ "space": " ",
31
+ "super": "Meta",
32
+ "tab": "Tab",
33
+ "win": "Meta",
34
+ }
35
+
36
+
37
+ class FleetPlaywrightWrapper:
38
+ """
39
+ A wrapper that adds Playwright browser automation to Fleet environment instances.
40
+
41
+ This class handles:
42
+ - Browser connection via CDP
43
+ - Computer actions (click, scroll, type, etc.)
44
+ - Screenshot capture
45
+ - Integration with OpenAI computer use API
46
+
47
+ Usage:
48
+ instance = await fleet.env.make(env_key="hubspot", version="v1.2.7")
49
+ browser = FleetPlaywrightWrapper(instance)
50
+ await browser.start()
51
+
52
+ # Use browser methods
53
+ screenshot = await browser.screenshot()
54
+ tools = [browser.openai_cua_tool]
55
+
56
+ # Clean up when done
57
+ await browser.close()
58
+ """
59
+
60
+ def get_environment(self):
61
+ return "browser"
62
+
63
+ def get_dimensions(self):
64
+ return (1920, 1080)
65
+
66
+ def __init__(
67
+ self,
68
+ env: AsyncEnvironment,
69
+ display_width: int = 1920,
70
+ display_height: int = 1080,
71
+ ):
72
+ """
73
+ Initialize the Fleet Playwright wrapper.
74
+
75
+ Args:
76
+ env: Fleet environment instance
77
+ display_width: Browser viewport width
78
+ display_height: Browser viewport height
79
+ """
80
+ self.env = env
81
+ self.display_width = display_width
82
+ self.display_height = display_height
83
+
84
+ self._playwright = None
85
+ self._browser: Browser | None = None
86
+ self._page: Page | None = None
87
+ self._started = False
88
+
89
+ async def start(self):
90
+ """Start the browser and establish connection."""
91
+ if self._started:
92
+ return
93
+
94
+ # Start Playwright
95
+ self._playwright = await async_playwright().start()
96
+
97
+ # Start browser on the Fleet instance
98
+ print("Starting browser...")
99
+ await self.env.browser().start()
100
+ cdp = await self.env.browser().describe()
101
+
102
+ # Connect to browser
103
+ self._browser = await self._playwright.chromium.connect_over_cdp(
104
+ cdp.cdp_browser_url
105
+ )
106
+ self._page = self._browser.contexts[0].pages[0]
107
+ await self._page.set_viewport_size(
108
+ {"width": self.display_width, "height": self.display_height}
109
+ )
110
+
111
+ self._started = True
112
+ print(f"Track agent: {cdp.cdp_devtools_url}")
113
+
114
+ async def close(self):
115
+ """Close the browser connection."""
116
+ if self._playwright:
117
+ await self._playwright.stop()
118
+ self._playwright = None
119
+ self._browser = None
120
+ self._page = None
121
+ self._started = False
122
+
123
+ def _ensure_started(self):
124
+ """Ensure browser is started before operations."""
125
+ if not self._started:
126
+ raise RuntimeError("Browser not started. Call await browser.start() first.")
127
+
128
+ @property
129
+ def openai_cua_tool(self) -> Dict[str, Any]:
130
+ """
131
+ Tool definition for OpenAI computer use API.
132
+
133
+ Returns:
134
+ Tool definition dict for use with OpenAI responses API
135
+ """
136
+ return {
137
+ "type": "computer_use_preview",
138
+ "display_width": self.display_width,
139
+ "display_height": self.display_height,
140
+ "environment": "browser",
141
+ }
142
+
143
+ async def screenshot(self) -> str:
144
+ """
145
+ Take a screenshot and return base64 encoded string.
146
+
147
+ Returns:
148
+ Base64 encoded PNG screenshot
149
+ """
150
+ self._ensure_started()
151
+
152
+ png_bytes = await self._page.screenshot(full_page=False)
153
+ return base64.b64encode(png_bytes).decode("utf-8")
154
+
155
+ def get_current_url(self) -> str:
156
+ """Get the current page URL."""
157
+ self._ensure_started()
158
+ return self._page.url
159
+
160
+ async def execute_computer_action(self, action: Dict[str, Any]) -> Dict[str, Any]:
161
+ """
162
+ Execute a computer action and return the result for OpenAI API.
163
+
164
+ Args:
165
+ action: Computer action dict from OpenAI response
166
+
167
+ Returns:
168
+ Result dict for computer_call_output
169
+ """
170
+ self._ensure_started()
171
+
172
+ action_type = action["type"]
173
+ action_args = {k: v for k, v in action.items() if k != "type"}
174
+
175
+ print(f"Executing: {action_type}({action_args})")
176
+
177
+ # Execute the action
178
+ if hasattr(self, f"_{action_type}"):
179
+ method = getattr(self, f"_{action_type}")
180
+ await method(**action_args)
181
+ else:
182
+ raise ValueError(f"Unsupported action type: {action_type}")
183
+
184
+ # Take screenshot after action
185
+ screenshot_base64 = await self.screenshot()
186
+
187
+ return {
188
+ "type": "input_image",
189
+ "image_url": f"data:image/png;base64,{screenshot_base64}",
190
+ "current_url": self.get_current_url(),
191
+ }
192
+
193
+ # Computer action implementations
194
+ async def _click(self, x: int, y: int, button: str = "left") -> None:
195
+ """Click at coordinates."""
196
+ self._ensure_started()
197
+ await self._page.mouse.click(x, y, button=button)
198
+
199
+ async def _double_click(self, x: int, y: int) -> None:
200
+ """Double-click at coordinates."""
201
+ self._ensure_started()
202
+ await self._page.mouse.dblclick(x, y)
203
+
204
+ async def _scroll(self, x: int, y: int, scroll_x: int, scroll_y: int) -> None:
205
+ """Scroll from coordinates."""
206
+ self._ensure_started()
207
+ await self._page.mouse.move(x, y)
208
+ await self._page.evaluate(f"window.scrollBy({scroll_x}, {scroll_y})")
209
+
210
+ async def _type(self, text: str) -> None:
211
+ """Type text."""
212
+ self._ensure_started()
213
+ await self._page.keyboard.type(text)
214
+
215
+ async def _keypress(self, keys: List[str]) -> None:
216
+ """Press key combination."""
217
+ self._ensure_started()
218
+ mapped_keys = [CUA_KEY_TO_PLAYWRIGHT_KEY.get(key.lower(), key) for key in keys]
219
+ for key in mapped_keys:
220
+ await self._page.keyboard.down(key)
221
+ for key in reversed(mapped_keys):
222
+ await self._page.keyboard.up(key)
223
+
224
+ async def _move(self, x: int, y: int) -> None:
225
+ """Move mouse to coordinates."""
226
+ self._ensure_started()
227
+ await self._page.mouse.move(x, y)
228
+
229
+ async def _drag(self, path: List[Dict[str, int]]) -> None:
230
+ """Drag mouse along path."""
231
+ self._ensure_started()
232
+ if not path:
233
+ return
234
+ await self._page.mouse.move(path[0]["x"], path[0]["y"])
235
+ await self._page.mouse.down()
236
+ for point in path[1:]:
237
+ await self._page.mouse.move(point["x"], point["y"])
238
+ await self._page.mouse.up()
239
+
240
+ async def _wait(self, ms: int = 1000) -> None:
241
+ """Wait for specified milliseconds."""
242
+ import asyncio
243
+
244
+ await asyncio.sleep(ms / 1000)
245
+
246
+ # Browser-specific actions
247
+ async def _goto(self, url: str) -> None:
248
+ """Navigate to URL."""
249
+ self._ensure_started()
250
+ try:
251
+ await self._page.goto(url)
252
+ except Exception as e:
253
+ print(f"Error navigating to {url}: {e}")
254
+
255
+ async def _back(self) -> None:
256
+ """Go back in browser history."""
257
+ self._ensure_started()
258
+ await self._page.go_back()
259
+
260
+ async def _forward(self) -> None:
261
+ """Go forward in browser history."""
262
+ self._ensure_started()
263
+ await self._page.go_forward()
264
+
265
+ async def _refresh(self) -> None:
266
+ """Refresh the page."""
267
+ self._ensure_started()
268
+ await self._page.reload()
269
+
270
+ # ------------------------------------------------------------------
271
+ # Public aliases (no leading underscore) expected by the Agent &
272
+ # OpenAI computer-use API. They forward directly to the underscored
273
+ # implementations above so the external interface matches the older
274
+ # BasePlaywrightComputer class.
275
+ # ------------------------------------------------------------------
276
+
277
+ # Mouse / keyboard actions
278
+ click = _click
279
+ double_click = _double_click
280
+ scroll = _scroll
281
+ type = _type # noqa: A003 – shadowing built-in for API compatibility
282
+ keypress = _keypress
283
+ move = _move
284
+ drag = _drag
285
+ wait = _wait
286
+
287
+ # Browser navigation actions
288
+ goto = _goto
289
+ back = _back
290
+ forward = _forward
291
+ refresh = _refresh
File without changes
@@ -0,0 +1,26 @@
1
+ from abc import ABC
2
+ from ..instance.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.value}://{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
24
+
25
+ def __repr__(self) -> str:
26
+ return f"Resource(uri={self.uri}, mode={self.mode.value})"
@@ -0,0 +1,41 @@
1
+ from typing import Optional
2
+ from ..instance.models import (
3
+ Resource as ResourceModel,
4
+ CDPDescribeResponse,
5
+ ChromeStartRequest,
6
+ ChromeStartResponse,
7
+ )
8
+ from .base import Resource
9
+
10
+ from typing import TYPE_CHECKING
11
+
12
+ if TYPE_CHECKING:
13
+ from ..instance.base import AsyncWrapper
14
+
15
+
16
+ class AsyncBrowserResource(Resource):
17
+ def __init__(self, resource: ResourceModel, client: "AsyncWrapper"):
18
+ super().__init__(resource)
19
+ self.client = client
20
+
21
+ async def start(self, width: int = 1920, height: int = 1080) -> CDPDescribeResponse:
22
+ response = await self.client.request(
23
+ "POST",
24
+ "/resources/cdp/start",
25
+ json=ChromeStartRequest(resolution=f"{width},{height}").model_dump(),
26
+ )
27
+ ChromeStartResponse(**response.json())
28
+ return await self.describe()
29
+
30
+ async def describe(self) -> CDPDescribeResponse:
31
+ response = await self.client.request("GET", "/resources/cdp/describe")
32
+ if response.status_code != 200:
33
+ await self.start()
34
+ response = await self.client.request("GET", "/resources/cdp/describe")
35
+ return CDPDescribeResponse(**response.json())
36
+
37
+ async def cdp_url(self) -> str:
38
+ return (await self.describe()).cdp_browser_url
39
+
40
+ async def devtools_url(self) -> str:
41
+ return (await self.describe()).cdp_devtools_url
@@ -0,0 +1,41 @@
1
+ from typing import Any, List, Optional
2
+ from ..instance.models import Resource as ResourceModel
3
+ from ..instance.models import DescribeResponse, QueryRequest, QueryResponse
4
+ from .base import Resource
5
+
6
+ from typing import TYPE_CHECKING
7
+
8
+ if TYPE_CHECKING:
9
+ from ..instance.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"/resources/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"/resources/sqlite/{self.resource.name}/query",
39
+ json=request.model_dump(),
40
+ )
41
+ return QueryResponse(**response.json())
fleet/base.py CHANGED
@@ -48,27 +48,4 @@ class SyncWrapper(BaseWrapper):
48
48
  params=params,
49
49
  json=json,
50
50
  **kwargs,
51
- )
52
-
53
-
54
- class AsyncWrapper(BaseWrapper):
55
- def __init__(self, *, httpx_client: httpx.AsyncClient, **kwargs):
56
- super().__init__(**kwargs)
57
- self.httpx_client = httpx_client
58
-
59
- async def request(
60
- self,
61
- method: str,
62
- url: str,
63
- params: Optional[Dict[str, Any]] = None,
64
- json: Optional[Any] = None,
65
- **kwargs,
66
- ) -> httpx.Response:
67
- return await self.httpx_client.request(
68
- method,
69
- f"{self.base_url}{url}",
70
- headers=self.get_headers(),
71
- params=params,
72
- json=json,
73
- **kwargs,
74
- )
51
+ )
fleet/client.py CHANGED
@@ -14,26 +14,24 @@
14
14
 
15
15
  """Fleet API Client for making HTTP requests to Fleet services."""
16
16
 
17
- import asyncio
18
17
  import os
19
18
  import httpx
20
19
  import logging
21
20
  from typing import Optional, List
22
21
 
23
- from .base import EnvironmentBase, AsyncWrapper, SyncWrapper
22
+ from .base import EnvironmentBase, SyncWrapper
24
23
  from .models import InstanceRequest, InstanceRecord, Environment as EnvironmentModel
25
24
 
26
25
  from .instance import (
27
26
  InstanceClient,
28
- AsyncInstanceClient,
29
27
  ResetRequest,
30
28
  ResetResponse,
31
29
  ValidatorType,
32
30
  ExecuteFunctionResponse,
33
31
  )
34
32
  from .resources.base import Resource
35
- from .resources.sqlite import AsyncSQLiteResource
36
- from .resources.browser import AsyncBrowserResource
33
+ from .resources.sqlite import SQLiteResource
34
+ from .resources.browser import BrowserResource
37
35
 
38
36
  logger = logging.getLogger(__name__)
39
37
 
@@ -41,7 +39,7 @@ logger = logging.getLogger(__name__)
41
39
  class Environment(EnvironmentBase):
42
40
  def __init__(self, httpx_client: Optional[httpx.Client] = None, **kwargs):
43
41
  super().__init__(**kwargs)
44
- self._httpx_client = httpx_client or httpx.Client()
42
+ self._httpx_client = httpx_client or httpx.Client(timeout=60.0)
45
43
  self._instance: Optional[InstanceClient] = None
46
44
 
47
45
  @property
@@ -50,46 +48,33 @@ class Environment(EnvironmentBase):
50
48
  self._instance = InstanceClient(self.manager_url, self._httpx_client)
51
49
  return self._instance
52
50
 
53
-
54
- class AsyncEnvironment(EnvironmentBase):
55
- def __init__(self, httpx_client: Optional[httpx.AsyncClient] = None, **kwargs):
56
- super().__init__(**kwargs)
57
- self._httpx_client = httpx_client or httpx.AsyncClient(timeout=60.0)
58
- self._instance: Optional[AsyncInstanceClient] = None
59
-
60
- @property
61
- def instance(self) -> AsyncInstanceClient:
62
- if self._instance is None:
63
- self._instance = AsyncInstanceClient(self.manager_url, self._httpx_client)
64
- return self._instance
65
-
66
- async def reset(
51
+ def reset(
67
52
  self, seed: Optional[int] = None, timestamp: Optional[int] = None
68
53
  ) -> ResetResponse:
69
- return await self.instance.reset(ResetRequest(seed=seed, timestamp=timestamp))
54
+ return self.instance.reset(ResetRequest(seed=seed, timestamp=timestamp))
70
55
 
71
- def db(self, name: str = "current") -> AsyncSQLiteResource:
56
+ def db(self, name: str = "current") -> SQLiteResource:
72
57
  return self.instance.db(name)
73
58
 
74
- def browser(self, name: str = "cdp") -> AsyncBrowserResource:
59
+ def browser(self, name: str = "cdp") -> BrowserResource:
75
60
  return self.instance.browser(name)
76
61
 
77
62
  def state(self, uri: str) -> Resource:
78
63
  return self.instance.state(uri)
79
64
 
80
- async def resources(self) -> List[Resource]:
81
- return await self.instance.resources()
65
+ def resources(self) -> List[Resource]:
66
+ return self.instance.resources()
82
67
 
83
- async def close(self) -> InstanceRecord:
84
- return await AsyncFleet().delete(self.instance_id)
68
+ def close(self) -> InstanceRecord:
69
+ return Fleet().delete(self.instance_id)
85
70
 
86
- async def verify(self, validator: ValidatorType) -> ExecuteFunctionResponse:
87
- return await self.instance.verify(validator)
71
+ def verify(self, validator: ValidatorType) -> ExecuteFunctionResponse:
72
+ return self.instance.verify(validator)
88
73
 
89
- async def verify_raw(
74
+ def verify_raw(
90
75
  self, function_code: str, function_name: str
91
76
  ) -> ExecuteFunctionResponse:
92
- return await self.instance.verify_raw(function_code, function_name)
77
+ return self.instance.verify_raw(function_code, function_name)
93
78
 
94
79
 
95
80
  class Fleet:
@@ -106,7 +91,7 @@ class Fleet:
106
91
  httpx_client=self._httpx_client,
107
92
  )
108
93
 
109
- def environments(self) -> List[EnvironmentModel]:
94
+ def list_envs(self) -> List[EnvironmentModel]:
110
95
  response = self.client.request("GET", "/v1/env/")
111
96
  return [EnvironmentModel(**env_data) for env_data in response.json()]
112
97
 
@@ -114,52 +99,7 @@ class Fleet:
114
99
  response = self.client.request("GET", f"/v1/env/{env_key}")
115
100
  return EnvironmentModel(**response.json())
116
101
 
117
- def make(self, request: InstanceRequest) -> Environment:
118
- response = self.client.request(
119
- "POST", "/v1/env/instances", json=request.model_dump()
120
- )
121
- return Environment(**response.json())
122
-
123
- def instances(self, status: Optional[str] = None) -> List[Environment]:
124
- params = {}
125
- if status:
126
- params["status"] = status
127
-
128
- response = self.client.request("GET", "/v1/env/instances", params=params)
129
- return [Environment(**instance_data) for instance_data in response.json()]
130
-
131
- def instance(self, instance_id: str) -> Environment:
132
- response = self.client.request("GET", f"/v1/env/instances/{instance_id}")
133
- return Environment(**response.json())
134
-
135
- def delete(self, instance_id: str) -> InstanceRecord:
136
- response = self.client.request("DELETE", f"/v1/env/instances/{instance_id}")
137
- return InstanceRecord(**response.json())
138
-
139
-
140
- class AsyncFleet:
141
- def __init__(
142
- self,
143
- api_key: Optional[str] = os.getenv("FLEET_API_KEY"),
144
- base_url: Optional[str] = None,
145
- httpx_client: Optional[httpx.AsyncClient] = None,
146
- ):
147
- self._httpx_client = httpx_client or httpx.AsyncClient(timeout=60.0)
148
- self.client = AsyncWrapper(
149
- api_key=api_key,
150
- base_url=base_url,
151
- httpx_client=self._httpx_client,
152
- )
153
-
154
- async def list_envs(self) -> List[EnvironmentModel]:
155
- response = await self.client.request("GET", "/v1/env/")
156
- return [EnvironmentModel(**env_data) for env_data in response.json()]
157
-
158
- async def environment(self, env_key: str) -> EnvironmentModel:
159
- response = await self.client.request("GET", f"/v1/env/{env_key}")
160
- return EnvironmentModel(**response.json())
161
-
162
- async def make(self, env_key: str) -> AsyncEnvironment:
102
+ def make(self, env_key: str) -> Environment:
163
103
  if ":" in env_key:
164
104
  env_key_part, version = env_key.split(":", 1)
165
105
  if not version.startswith("v"):
@@ -169,33 +109,25 @@ class AsyncFleet:
169
109
  version = None
170
110
 
171
111
  request = InstanceRequest(env_key=env_key_part, version=version)
172
- response = await self.client.request(
112
+ response = self.client.request(
173
113
  "POST", "/v1/env/instances", json=request.model_dump()
174
114
  )
175
- instance = AsyncEnvironment(**response.json())
176
- await instance.instance.load()
115
+ instance = Environment(**response.json())
116
+ instance.instance.load()
177
117
  return instance
178
118
 
179
- async def instances(self, status: Optional[str] = None) -> List[AsyncEnvironment]:
119
+ def instances(self, status: Optional[str] = None) -> List[Environment]:
180
120
  params = {}
181
121
  if status:
182
122
  params["status"] = status
183
123
 
184
- response = await self.client.request("GET", "/v1/env/instances", params=params)
185
- instances = [
186
- AsyncEnvironment(**instance_data) for instance_data in response.json()
187
- ]
188
- await asyncio.gather(*[instance.instance.load() for instance in instances])
189
- return instances
190
-
191
- async def instance(self, instance_id: str) -> AsyncEnvironment:
192
- response = await self.client.request("GET", f"/v1/env/instances/{instance_id}")
193
- instance = AsyncEnvironment(**response.json())
194
- await instance.instance.load()
195
- return instance
124
+ response = self.client.request("GET", "/v1/env/instances", params=params)
125
+ return [Environment(**instance_data) for instance_data in response.json()]
196
126
 
197
- async def delete(self, instance_id: str) -> InstanceRecord:
198
- response = await self.client.request(
199
- "DELETE", f"/v1/env/instances/{instance_id}"
200
- )
201
- return InstanceRecord(**response.json())
127
+ def instance(self, instance_id: str) -> Environment:
128
+ response = self.client.request("GET", f"/v1/env/instances/{instance_id}")
129
+ return Environment(**response.json())
130
+
131
+ def delete(self, instance_id: str) -> InstanceRecord:
132
+ response = self.client.request("DELETE", f"/v1/env/instances/{instance_id}")
133
+ return InstanceRecord(**response.json())
fleet/env/__init__.py CHANGED
@@ -1,3 +1,15 @@
1
+ """Fleet env module - convenience functions for environment management."""
2
+
1
3
  from .client import make, list_envs, get
2
4
 
3
- __all__ = ["make", "list_envs", "get"]
5
+ # Import async versions from _async
6
+ from .._async.env.client import make_async, list_envs_async, get_async
7
+
8
+ __all__ = [
9
+ "make",
10
+ "list_envs",
11
+ "get",
12
+ "make_async",
13
+ "list_envs_async",
14
+ "get_async",
15
+ ]
fleet/env/client.py CHANGED
@@ -1,15 +1,15 @@
1
- from ..client import AsyncFleet, AsyncEnvironment
1
+ from ..client import Fleet, Environment
2
2
  from ..models import Environment as EnvironmentModel
3
3
  from typing import List
4
4
 
5
5
 
6
- async def make(env_key: str) -> AsyncEnvironment:
7
- return await AsyncFleet().make(env_key)
6
+ def make(env_key: str) -> Environment:
7
+ return Fleet().make(env_key)
8
8
 
9
9
 
10
- async def list_envs() -> List[EnvironmentModel]:
11
- return await AsyncFleet().list_envs()
10
+ def list_envs() -> List[EnvironmentModel]:
11
+ return Fleet().list_envs()
12
12
 
13
13
 
14
- async def get(instance_id: str) -> AsyncEnvironment:
15
- return await AsyncFleet().instance(instance_id)
14
+ def get(instance_id: str) -> Environment:
15
+ return Fleet().instance(instance_id)