hopx-ai 0.1.17__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 hopx-ai might be problematic. Click here for more details.

@@ -0,0 +1,151 @@
1
+ """Async environment variables for sandboxes."""
2
+
3
+ from typing import Dict, Optional
4
+ import logging
5
+ from ._async_agent_client import AsyncAgentHTTPClient
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+
10
+ class AsyncEnvironmentVariables:
11
+ """
12
+ Async environment variable operations.
13
+
14
+ Provides methods for managing environment variables inside the sandbox at runtime.
15
+
16
+ Features:
17
+ - Get all environment variables
18
+ - Set/replace all environment variables
19
+ - Update specific environment variables (merge)
20
+ - Delete individual environment variables
21
+ """
22
+
23
+ def __init__(self, sandbox):
24
+ """Initialize with sandbox reference."""
25
+ self._sandbox = sandbox
26
+ logger.debug("AsyncEnvironmentVariables initialized")
27
+
28
+ async def _get_client(self) -> AsyncAgentHTTPClient:
29
+ """Get agent client from sandbox."""
30
+ await self._sandbox._ensure_agent_client()
31
+ return self._sandbox._agent_client
32
+
33
+ async def get_all(self, *, timeout: Optional[int] = None) -> Dict[str, str]:
34
+ """
35
+ Get all environment variables.
36
+
37
+ Args:
38
+ timeout: Request timeout in seconds (overrides default)
39
+
40
+ Returns:
41
+ Dictionary of environment variables
42
+ """
43
+ logger.debug("Getting all environment variables")
44
+
45
+ client = await self._get_client()
46
+ response = await client.get(
47
+ "/env",
48
+ operation="get environment variables"
49
+ )
50
+
51
+ return response.get("env_vars", {})
52
+
53
+ async def set_all(
54
+ self,
55
+ env_vars: Dict[str, str],
56
+ *,
57
+ timeout: Optional[int] = None
58
+ ) -> Dict[str, str]:
59
+ """
60
+ Set/replace all environment variables.
61
+
62
+ This replaces ALL existing environment variables with the provided ones.
63
+ Use update() if you want to merge instead.
64
+
65
+ Args:
66
+ env_vars: Dictionary of environment variables to set
67
+ timeout: Request timeout in seconds
68
+
69
+ Returns:
70
+ Updated dictionary of all environment variables
71
+ """
72
+ logger.debug(f"Setting {len(env_vars)} environment variables (replace all)")
73
+
74
+ client = await self._get_client()
75
+ response = await client.put(
76
+ "/env",
77
+ json={"env_vars": env_vars},
78
+ operation="set environment variables"
79
+ )
80
+
81
+ return response.get("env_vars", env_vars)
82
+
83
+ async def update(
84
+ self,
85
+ env_vars: Dict[str, str],
86
+ *,
87
+ timeout: Optional[int] = None
88
+ ) -> Dict[str, str]:
89
+ """
90
+ Update specific environment variables (merge).
91
+
92
+ This merges the provided variables with existing ones.
93
+ Existing variables not specified are preserved.
94
+
95
+ Args:
96
+ env_vars: Dictionary of environment variables to update/add
97
+ timeout: Request timeout in seconds
98
+
99
+ Returns:
100
+ Updated dictionary of all environment variables
101
+ """
102
+ logger.debug(f"Updating {len(env_vars)} environment variables (merge)")
103
+
104
+ client = await self._get_client()
105
+ response = await client.patch(
106
+ "/env",
107
+ json={"env_vars": env_vars, "merge": True}, # ✅ FIXED: add merge flag
108
+ operation="update environment variables"
109
+ )
110
+
111
+ # Agent returns 204 No Content - get updated vars
112
+ if not response or not response.get("env_vars"):
113
+ return await self.get_all()
114
+
115
+ return response.get("env_vars", {})
116
+
117
+ async def delete(self, name: str, *, timeout: Optional[int] = None) -> None:
118
+ """
119
+ Delete a specific environment variable.
120
+
121
+ Note: Agent's DELETE /env clears ALL custom variables.
122
+ We work around this by re-setting all vars except the one to delete.
123
+
124
+ Args:
125
+ name: Variable name to delete
126
+ timeout: Request timeout in seconds
127
+ """
128
+ logger.debug(f"Deleting environment variable: {name}")
129
+
130
+ # Get all current env vars
131
+ current_vars = await self.get_all()
132
+
133
+ # Remove the specified variable
134
+ if name in current_vars:
135
+ del current_vars[name]
136
+ # Re-set all env vars without the deleted one
137
+ await self.set_all(current_vars)
138
+ logger.debug(f"Environment variable {name} deleted")
139
+ else:
140
+ logger.debug(f"Environment variable {name} not found (already deleted)")
141
+
142
+ # Convenience methods (aliases)
143
+
144
+ async def get(self, name: str) -> Optional[str]:
145
+ """Get a single environment variable value."""
146
+ all_vars = await self.get_all()
147
+ return all_vars.get(name)
148
+
149
+ async def set(self, name: str, value: str) -> None:
150
+ """Set a single environment variable (convenience method)."""
151
+ await self.update({name: value})
@@ -0,0 +1,81 @@
1
+ """Async file operations for sandboxes."""
2
+
3
+ from typing import Optional, List, Dict, Any, AsyncIterator
4
+ import logging
5
+ from ._async_agent_client import AsyncAgentHTTPClient
6
+
7
+ logger = logging.getLogger(__name__)
8
+
9
+
10
+ class AsyncFiles:
11
+ """Async file operations for sandboxes."""
12
+
13
+ def __init__(self, sandbox):
14
+ """Initialize with sandbox reference."""
15
+ self._sandbox = sandbox
16
+ logger.debug("AsyncFiles initialized")
17
+
18
+ async def _get_client(self) -> AsyncAgentHTTPClient:
19
+ """Get agent client from sandbox."""
20
+ await self._sandbox._ensure_agent_client()
21
+ return self._sandbox._agent_client
22
+
23
+ async def write(self, path: str, content: str) -> None:
24
+ """Write content to file."""
25
+ client = await self._get_client()
26
+ await client.post(
27
+ "/files/write",
28
+ json={"path": path, "content": content},
29
+ operation="write file",
30
+ context={"path": path}
31
+ )
32
+
33
+ async def read(self, path: str) -> str:
34
+ """Read file content."""
35
+ client = await self._get_client()
36
+ response = await client.get(
37
+ f"/files/read?path={path}",
38
+ operation="read file",
39
+ context={"path": path}
40
+ )
41
+ return response.get("content", "")
42
+
43
+ async def list(self, path: str = "/") -> List[Dict[str, Any]]:
44
+ """List files in directory."""
45
+ client = await self._get_client()
46
+ response = await client.get(
47
+ f"/files/list?path={path}",
48
+ operation="list files",
49
+ context={"path": path}
50
+ )
51
+ return response.get("files", [])
52
+
53
+ async def exists(self, path: str) -> bool:
54
+ """Check if file exists."""
55
+ client = await self._get_client()
56
+ response = await client.get(
57
+ f"/files/exists?path={path}",
58
+ operation="check file exists",
59
+ context={"path": path}
60
+ )
61
+ return response.get("exists", False)
62
+
63
+ async def mkdir(self, path: str, parents: bool = True) -> None:
64
+ """Create directory."""
65
+ client = await self._get_client()
66
+ await client.post(
67
+ "/files/mkdir",
68
+ json={"path": path, "parents": parents},
69
+ operation="create directory",
70
+ context={"path": path}
71
+ )
72
+
73
+ async def remove(self, path: str, recursive: bool = False) -> None:
74
+ """Remove file or directory."""
75
+ client = await self._get_client()
76
+ await client.post(
77
+ "/files/remove",
78
+ json={"path": path, "recursive": recursive},
79
+ operation="remove file",
80
+ context={"path": path}
81
+ )