kolega-code 0.1.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.
- kolega_code/__init__.py +151 -0
- kolega_code/agent/__init__.py +42 -0
- kolega_code/agent/baseagent.py +998 -0
- kolega_code/agent/browseragent.py +123 -0
- kolega_code/agent/coder.py +157 -0
- kolega_code/agent/common.py +41 -0
- kolega_code/agent/compression.py +81 -0
- kolega_code/agent/context.py +112 -0
- kolega_code/agent/conversation.py +408 -0
- kolega_code/agent/generalagent.py +146 -0
- kolega_code/agent/investigationagent.py +123 -0
- kolega_code/agent/planningagent.py +187 -0
- kolega_code/agent/prompt_provider.py +196 -0
- kolega_code/agent/prompt_templates/agents/browser.j2 +102 -0
- kolega_code/agent/prompt_templates/agents/coder_cli_mode.j2 +127 -0
- kolega_code/agent/prompt_templates/agents/general.j2 +68 -0
- kolega_code/agent/prompt_templates/agents/investigation.j2 +72 -0
- kolega_code/agent/prompt_templates/common/frontend_guidance.md +36 -0
- kolega_code/agent/prompt_templates/common/kolega_md_instructions.md +14 -0
- kolega_code/agent/prompt_templates/environment_variables/workspace_env_vars.md +11 -0
- kolega_code/agent/prompt_templates/template_guidance/expo-template.md +379 -0
- kolega_code/agent/prompt_templates/template_guidance/html-website-template.md +3 -0
- kolega_code/agent/prompt_templates/template_guidance/mern-stack-template.md +3 -0
- kolega_code/agent/prompt_templates/template_guidance/react-vite-shadcdn-template.md +182 -0
- kolega_code/agent/prompts.py +192 -0
- kolega_code/agent/tests/__init__.py +0 -0
- kolega_code/agent/tests/llm/__init__.py +0 -0
- kolega_code/agent/tests/llm/test_anthropic_token_counting.py +633 -0
- kolega_code/agent/tests/llm/test_billing_openai_cache.py +74 -0
- kolega_code/agent/tests/llm/test_client.py +773 -0
- kolega_code/agent/tests/llm/test_dashscope_mapping.py +32 -0
- kolega_code/agent/tests/llm/test_error_boundary.py +322 -0
- kolega_code/agent/tests/llm/test_exceptions.py +249 -0
- kolega_code/agent/tests/llm/test_instrumented_client.py +536 -0
- kolega_code/agent/tests/llm/test_instrumented_client_integration.py +547 -0
- kolega_code/agent/tests/llm/test_langfuse_normalization.py +39 -0
- kolega_code/agent/tests/llm/test_model_specs.py +17 -0
- kolega_code/agent/tests/llm/test_openai_cached_tokens.py +58 -0
- kolega_code/agent/tests/llm/test_openai_cached_tokens_stream.py +74 -0
- kolega_code/agent/tests/llm/test_openai_message_conversion.py +30 -0
- kolega_code/agent/tests/llm/test_openai_token_counting.py +687 -0
- kolega_code/agent/tests/llm/test_tool_execution_ids.py +193 -0
- kolega_code/agent/tests/services/__init__.py +1 -0
- kolega_code/agent/tests/services/test_browser.py +447 -0
- kolega_code/agent/tests/services/test_browser_parity.py +353 -0
- kolega_code/agent/tests/services/test_file_system.py +699 -0
- kolega_code/agent/tests/services/test_sandbox_terminal_input.py +98 -0
- kolega_code/agent/tests/services/test_terminal.py +154 -0
- kolega_code/agent/tests/services/test_terminal_command_tracking.py +385 -0
- kolega_code/agent/tests/services/test_terminal_state_serializer.py +262 -0
- kolega_code/agent/tests/test_agent_tools_inventory.py +267 -0
- kolega_code/agent/tests/test_base_agent.py +1942 -0
- kolega_code/agent/tests/test_coder_attachments.py +330 -0
- kolega_code/agent/tests/test_coder_prompt_extensions.py +61 -0
- kolega_code/agent/tests/test_commands.py +179 -0
- kolega_code/agent/tests/test_duplicate_tool_results.py +556 -0
- kolega_code/agent/tests/test_empty_message_handling.py +48 -0
- kolega_code/agent/tests/test_general_agent.py +242 -0
- kolega_code/agent/tests/test_html.py +320 -0
- kolega_code/agent/tests/test_parallel_tool_calls.py +291 -0
- kolega_code/agent/tests/test_planning_agent.py +227 -0
- kolega_code/agent/tests/test_prompt_provider.py +271 -0
- kolega_code/agent/tests/test_tool_registry.py +102 -0
- kolega_code/agent/tests/test_tools.py +549 -0
- kolega_code/agent/tests/tool_backend/__init__.py +0 -0
- kolega_code/agent/tests/tool_backend/test_agent_tool.py +356 -0
- kolega_code/agent/tests/tool_backend/test_base_tool.py +147 -0
- kolega_code/agent/tests/tool_backend/test_browser_tool.py +335 -0
- kolega_code/agent/tests/tool_backend/test_build_tool.py +93 -0
- kolega_code/agent/tests/tool_backend/test_create_file_tool.py +115 -0
- kolega_code/agent/tests/tool_backend/test_glob_tool.py +196 -0
- kolega_code/agent/tests/tool_backend/test_glob_tool_sandbox_parity.py +230 -0
- kolega_code/agent/tests/tool_backend/test_list_directory_tool.py +292 -0
- kolega_code/agent/tests/tool_backend/test_read_file_tool.py +173 -0
- kolega_code/agent/tests/tool_backend/test_replace_entire_file_tool.py +115 -0
- kolega_code/agent/tests/tool_backend/test_replace_lines_tool.py +141 -0
- kolega_code/agent/tests/tool_backend/test_search_and_replace_tool.py +174 -0
- kolega_code/agent/tests/tool_backend/test_search_codebase_tool.py +228 -0
- kolega_code/agent/tests/tool_backend/test_terminal_tool.py +482 -0
- kolega_code/agent/tests/tool_backend/test_think_hard_integration.py +189 -0
- kolega_code/agent/tests/tool_backend/test_think_hard_streaming.py +445 -0
- kolega_code/agent/tests/tool_backend/test_web_fetch_tool.py +194 -0
- kolega_code/agent/tool_backend/agent_tool.py +414 -0
- kolega_code/agent/tool_backend/apply_edit_tool.py +98 -0
- kolega_code/agent/tool_backend/apply_patch_tool.py +514 -0
- kolega_code/agent/tool_backend/base_tool.py +217 -0
- kolega_code/agent/tool_backend/browser_tool.py +271 -0
- kolega_code/agent/tool_backend/build_tool.py +93 -0
- kolega_code/agent/tool_backend/create_file_tool.py +52 -0
- kolega_code/agent/tool_backend/glob_tool.py +323 -0
- kolega_code/agent/tool_backend/list_directory_tool.py +300 -0
- kolega_code/agent/tool_backend/memory_tool.py +79 -0
- kolega_code/agent/tool_backend/read_file_tool.py +119 -0
- kolega_code/agent/tool_backend/replace_entire_file_tool.py +40 -0
- kolega_code/agent/tool_backend/replace_lines_tool.py +97 -0
- kolega_code/agent/tool_backend/search_and_replace_tool.py +146 -0
- kolega_code/agent/tool_backend/search_codebase_tool.py +377 -0
- kolega_code/agent/tool_backend/streaming_tool.py +47 -0
- kolega_code/agent/tool_backend/terminal_tool.py +643 -0
- kolega_code/agent/tool_backend/think_hard_tool.py +211 -0
- kolega_code/agent/tool_backend/web_fetch_tool.py +205 -0
- kolega_code/agent/tools.py +1704 -0
- kolega_code/agent/utils/commands.py +94 -0
- kolega_code/cli/__init__.py +1 -0
- kolega_code/cli/app.py +2756 -0
- kolega_code/cli/config.py +280 -0
- kolega_code/cli/connection.py +49 -0
- kolega_code/cli/file_index.py +147 -0
- kolega_code/cli/main.py +564 -0
- kolega_code/cli/mentions.py +155 -0
- kolega_code/cli/messages.py +89 -0
- kolega_code/cli/provider_registry.py +96 -0
- kolega_code/cli/session_store.py +207 -0
- kolega_code/cli/settings.py +87 -0
- kolega_code/cli/skills.py +409 -0
- kolega_code/cli/slash_commands.py +108 -0
- kolega_code/cli/tests/__init__.py +1 -0
- kolega_code/cli/tests/test_app.py +4251 -0
- kolega_code/cli/tests/test_cli_config.py +171 -0
- kolega_code/cli/tests/test_connection.py +26 -0
- kolega_code/cli/tests/test_file_index.py +103 -0
- kolega_code/cli/tests/test_main.py +455 -0
- kolega_code/cli/tests/test_mentions.py +108 -0
- kolega_code/cli/tests/test_session_store.py +67 -0
- kolega_code/cli/tests/test_settings.py +62 -0
- kolega_code/cli/tests/test_skills.py +157 -0
- kolega_code/cli/tests/test_slash_commands.py +88 -0
- kolega_code/cli/theme.py +180 -0
- kolega_code/config.py +154 -0
- kolega_code/events.py +202 -0
- kolega_code/llm/client.py +300 -0
- kolega_code/llm/exceptions.py +285 -0
- kolega_code/llm/instrumented_client.py +520 -0
- kolega_code/llm/models.py +1368 -0
- kolega_code/llm/providers/__init__.py +0 -0
- kolega_code/llm/providers/anthropic.py +387 -0
- kolega_code/llm/providers/base.py +71 -0
- kolega_code/llm/providers/google.py +157 -0
- kolega_code/llm/providers/models.py +37 -0
- kolega_code/llm/providers/openai.py +363 -0
- kolega_code/llm/ratelimit.py +40 -0
- kolega_code/llm/specs.py +67 -0
- kolega_code/llm/tool_execution_ids.py +18 -0
- kolega_code/models/__init__.py +9 -0
- kolega_code/models/sandbox_terminal_state.py +47 -0
- kolega_code/runtime.py +50 -0
- kolega_code/sandbox/README.md +200 -0
- kolega_code/sandbox/__init__.py +21 -0
- kolega_code/sandbox/async_filesystem.py +475 -0
- kolega_code/sandbox/base.py +297 -0
- kolega_code/sandbox/browser.py +25 -0
- kolega_code/sandbox/event_loop.py +43 -0
- kolega_code/sandbox/filesystem.py +341 -0
- kolega_code/sandbox/local.py +118 -0
- kolega_code/sandbox/serializer.py +175 -0
- kolega_code/sandbox/terminal.py +868 -0
- kolega_code/sandbox/utils.py +216 -0
- kolega_code/services/base.py +255 -0
- kolega_code/services/browser.py +444 -0
- kolega_code/services/file_system.py +749 -0
- kolega_code/services/html.py +221 -0
- kolega_code/services/terminal.py +903 -0
- kolega_code/tools/__init__.py +22 -0
- kolega_code/tools/core.py +33 -0
- kolega_code/tools/definitions.py +81 -0
- kolega_code/tools/registry.py +73 -0
- kolega_code-0.1.0.dist-info/METADATA +157 -0
- kolega_code-0.1.0.dist-info/RECORD +171 -0
- kolega_code-0.1.0.dist-info/WHEEL +4 -0
- kolega_code-0.1.0.dist-info/entry_points.txt +2 -0
- kolega_code-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
"""Base interfaces for sandbox management."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from typing import Any, Dict, List, Optional, Protocol, runtime_checkable
|
|
6
|
+
|
|
7
|
+
from ..services.base import TerminalManager, BrowserManager
|
|
8
|
+
from ..services.file_system import FileSystem
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@runtime_checkable
|
|
12
|
+
class SandboxHandle(Protocol):
|
|
13
|
+
"""
|
|
14
|
+
The duck-typed interface the generic sandbox services expect from a
|
|
15
|
+
provider's sandbox object (e.g. E2B's AsyncSandbox).
|
|
16
|
+
|
|
17
|
+
SandboxFileSystem, SandboxTerminalManager, and friends only touch these
|
|
18
|
+
members; a provider object satisfying this protocol can back them all.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
@property
|
|
22
|
+
def commands(self) -> Any:
|
|
23
|
+
"""Command runner exposing run(...), and send_stdin(pid, data) where supported."""
|
|
24
|
+
...
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def files(self) -> Any:
|
|
28
|
+
"""File API exposing read/write/list/exists operations."""
|
|
29
|
+
...
|
|
30
|
+
|
|
31
|
+
def get_host(self, port: int) -> str:
|
|
32
|
+
"""Return the externally reachable host for a port inside the sandbox."""
|
|
33
|
+
...
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@dataclass
|
|
37
|
+
class SandboxConfig:
|
|
38
|
+
"""Configuration for sandbox creation."""
|
|
39
|
+
|
|
40
|
+
git_url: str
|
|
41
|
+
branch: str = "main"
|
|
42
|
+
commit_hash: Optional[str] = None
|
|
43
|
+
manifest_path: str = ".kolega-manifest.yaml"
|
|
44
|
+
resources: Dict[str, Any] = field(default_factory=dict)
|
|
45
|
+
environment_vars: Dict[str, str] = field(default_factory=dict)
|
|
46
|
+
auth_method: str = "group_token" # group_token, pat, or ssh
|
|
47
|
+
network_access_mode: str = "deny_all" # deny_all | allow_all | custom
|
|
48
|
+
network_allowed_hosts: List[str] = field(default_factory=list)
|
|
49
|
+
# Pool mode flags
|
|
50
|
+
skip_git: bool = False
|
|
51
|
+
skip_s3_mount: bool = False
|
|
52
|
+
skip_project_setup: bool = False
|
|
53
|
+
skip_integration_env_sync: bool = False # Skip user-specific environment variable sync
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@dataclass
|
|
57
|
+
class ProjectManifest:
|
|
58
|
+
"""Project configuration manifest."""
|
|
59
|
+
|
|
60
|
+
name: str
|
|
61
|
+
runtime: str # e.g., "node:18", "python:3.11"
|
|
62
|
+
install_commands: Optional[List[str]] = None
|
|
63
|
+
post_setup_commands: Optional[List[str]] = None
|
|
64
|
+
dev_server_command: Optional[str] = None
|
|
65
|
+
dev_server_commands: Optional[List[str]] = None
|
|
66
|
+
test_commands: Optional[List[str]] = None
|
|
67
|
+
build_command: Optional[str] = None
|
|
68
|
+
backend_build_command: Optional[str] = None
|
|
69
|
+
frontend_build_command: Optional[str] = None
|
|
70
|
+
environment_setup: Optional[List[str]] = None
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
class SandboxManager(ABC):
|
|
74
|
+
"""Abstract base class for sandbox managers."""
|
|
75
|
+
|
|
76
|
+
@abstractmethod
|
|
77
|
+
async def create_sandbox(
|
|
78
|
+
self,
|
|
79
|
+
workspace_id: str,
|
|
80
|
+
thread_id: str,
|
|
81
|
+
config: Optional[SandboxConfig] = None,
|
|
82
|
+
workspace: Optional[Any] = None,
|
|
83
|
+
connection_manager: Optional[Any] = None,
|
|
84
|
+
) -> str:
|
|
85
|
+
"""
|
|
86
|
+
Create a new sandbox.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
workspace_id: ID of the workspace
|
|
90
|
+
thread_id: Thread ID for terminal output streaming
|
|
91
|
+
config: Sandbox configuration
|
|
92
|
+
workspace: Optional workspace object for additional context
|
|
93
|
+
connection_manager: Optional connection manager for dispatching events
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
Sandbox ID
|
|
97
|
+
"""
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
@abstractmethod
|
|
101
|
+
async def destroy_sandbox(self, sandbox_id: str) -> None:
|
|
102
|
+
"""
|
|
103
|
+
Destroy a sandbox and clean up resources.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
sandbox_id: ID of the sandbox to destroy
|
|
107
|
+
|
|
108
|
+
Raises:
|
|
109
|
+
ValueError: If sandbox doesn't exist
|
|
110
|
+
"""
|
|
111
|
+
|
|
112
|
+
@abstractmethod
|
|
113
|
+
async def get_sandbox_status(self, sandbox_id: str) -> Dict[str, Any]:
|
|
114
|
+
"""
|
|
115
|
+
Get current status of a sandbox.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
sandbox_id: ID of the sandbox
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
Dictionary with status information
|
|
122
|
+
|
|
123
|
+
Raises:
|
|
124
|
+
ValueError: If sandbox doesn't exist
|
|
125
|
+
"""
|
|
126
|
+
|
|
127
|
+
@abstractmethod
|
|
128
|
+
async def commit_changes(self, sandbox_id: str, message: str, files: Optional[List[str]] = None) -> str:
|
|
129
|
+
"""
|
|
130
|
+
Commit changes in sandbox and return commit hash.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
sandbox_id: ID of the sandbox
|
|
134
|
+
message: Commit message
|
|
135
|
+
files: Optional list of files to commit (None = all changes)
|
|
136
|
+
|
|
137
|
+
Returns:
|
|
138
|
+
Git commit hash
|
|
139
|
+
|
|
140
|
+
Raises:
|
|
141
|
+
ValueError: If sandbox doesn't exist
|
|
142
|
+
Exception: If commit fails
|
|
143
|
+
"""
|
|
144
|
+
|
|
145
|
+
@abstractmethod
|
|
146
|
+
async def push_changes(self, sandbox_id: str) -> bool:
|
|
147
|
+
"""
|
|
148
|
+
Push committed changes to remote repository.
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
sandbox_id: ID of the sandbox
|
|
152
|
+
|
|
153
|
+
Returns:
|
|
154
|
+
True if push succeeded, False otherwise
|
|
155
|
+
|
|
156
|
+
Raises:
|
|
157
|
+
ValueError: If sandbox doesn't exist
|
|
158
|
+
"""
|
|
159
|
+
|
|
160
|
+
@abstractmethod
|
|
161
|
+
def get_filesystem(self, sandbox_id: str) -> FileSystem:
|
|
162
|
+
"""
|
|
163
|
+
Get filesystem interface for sandbox.
|
|
164
|
+
|
|
165
|
+
Args:
|
|
166
|
+
sandbox_id: ID of the sandbox
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
FileSystem implementation for the sandbox
|
|
170
|
+
|
|
171
|
+
Raises:
|
|
172
|
+
ValueError: If sandbox doesn't exist
|
|
173
|
+
"""
|
|
174
|
+
|
|
175
|
+
@abstractmethod
|
|
176
|
+
def get_terminal_manager(self, sandbox_id: str) -> TerminalManager:
|
|
177
|
+
"""
|
|
178
|
+
Get terminal manager for sandbox.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
sandbox_id: ID of the sandbox
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
TerminalManager implementation for the sandbox
|
|
185
|
+
|
|
186
|
+
Raises:
|
|
187
|
+
ValueError: If sandbox doesn't exist
|
|
188
|
+
"""
|
|
189
|
+
|
|
190
|
+
@abstractmethod
|
|
191
|
+
def get_browser_manager(self, sandbox_id: str) -> BrowserManager:
|
|
192
|
+
"""
|
|
193
|
+
Get browser manager for sandbox.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
sandbox_id: ID of the sandbox
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
BrowserManager implementation for the sandbox
|
|
200
|
+
|
|
201
|
+
Raises:
|
|
202
|
+
ValueError: If sandbox doesn't exist
|
|
203
|
+
"""
|
|
204
|
+
|
|
205
|
+
@abstractmethod
|
|
206
|
+
async def get_host(self, sandbox_id: str, port: int) -> str:
|
|
207
|
+
"""
|
|
208
|
+
Get the hostname for accessing services on the given port.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
sandbox_id: ID of the sandbox
|
|
212
|
+
port: The port number to access
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
The hostname (e.g., 'localhost' or 'xxxx.e2b.dev')
|
|
216
|
+
"""
|
|
217
|
+
pass
|
|
218
|
+
|
|
219
|
+
@abstractmethod
|
|
220
|
+
async def pause_sandbox(self, sandbox_id: str) -> str:
|
|
221
|
+
"""
|
|
222
|
+
Pause sandbox and return persistent sandbox ID for resuming later.
|
|
223
|
+
|
|
224
|
+
Args:
|
|
225
|
+
sandbox_id: ID of the sandbox to pause
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
Persistent sandbox ID that can be used to resume
|
|
229
|
+
|
|
230
|
+
Raises:
|
|
231
|
+
ValueError: If sandbox doesn't exist
|
|
232
|
+
"""
|
|
233
|
+
pass
|
|
234
|
+
|
|
235
|
+
@abstractmethod
|
|
236
|
+
async def resume_sandbox(self, persistent_sandbox_id: str, workspace_id: str, thread_id: str) -> str:
|
|
237
|
+
"""
|
|
238
|
+
Resume a paused sandbox from persistent ID.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
persistent_sandbox_id: The persistent ID returned from pause_sandbox
|
|
242
|
+
workspace_id: ID of the workspace
|
|
243
|
+
thread_id: Thread ID for terminal output streaming
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
New sandbox ID for the resumed sandbox
|
|
247
|
+
|
|
248
|
+
Raises:
|
|
249
|
+
Exception: If sandbox cannot be resumed
|
|
250
|
+
"""
|
|
251
|
+
pass
|
|
252
|
+
|
|
253
|
+
@abstractmethod
|
|
254
|
+
def has_sandbox(self, sandbox_id: str) -> bool:
|
|
255
|
+
"""
|
|
256
|
+
Check if a sandbox is currently active in memory.
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
sandbox_id: ID of the sandbox to check
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
True if sandbox is active, False otherwise
|
|
263
|
+
"""
|
|
264
|
+
pass
|
|
265
|
+
|
|
266
|
+
@abstractmethod
|
|
267
|
+
async def adopt_sandbox(self, sandbox_id: str, workspace_id: str, thread_id: str) -> str:
|
|
268
|
+
"""
|
|
269
|
+
Adopt an existing sandbox into this manager instance.
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
sandbox_id: The sandbox ID to adopt
|
|
273
|
+
workspace_id: ID of the workspace
|
|
274
|
+
thread_id: Thread ID for terminal output streaming
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
The sandbox ID (same as input)
|
|
278
|
+
|
|
279
|
+
Raises:
|
|
280
|
+
Exception: If sandbox cannot be connected to
|
|
281
|
+
"""
|
|
282
|
+
pass
|
|
283
|
+
|
|
284
|
+
@abstractmethod
|
|
285
|
+
async def sync_sandbox_env_vars(self, sandbox_id: str, workspace_id: str, sandbox: Optional[Any] = None, skip_integration_env_sync: bool = False) -> None:
|
|
286
|
+
"""
|
|
287
|
+
Sync current environment variables to an existing sandbox.
|
|
288
|
+
|
|
289
|
+
Args:
|
|
290
|
+
sandbox_id: ID of the sandbox
|
|
291
|
+
workspace_id: ID of the workspace (used to fetch env vars from database)
|
|
292
|
+
sandbox: Optional sandbox instance (avoids reconnection if provided)
|
|
293
|
+
|
|
294
|
+
Raises:
|
|
295
|
+
Exception: If sandbox cannot be connected to or env vars cannot be synced
|
|
296
|
+
"""
|
|
297
|
+
pass
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Browser manager implementation for sandbox environments."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from ..services.browser import PlaywrightBrowserManager
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class SandboxBrowserManager(PlaywrightBrowserManager):
|
|
9
|
+
"""
|
|
10
|
+
Browser manager for sandbox environments using Browserless.
|
|
11
|
+
|
|
12
|
+
This class extends PlaywrightBrowserManager with Browserless support enabled,
|
|
13
|
+
providing remote browser capabilities for sandbox environments.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
def __init__(self, sandbox: Any = None):
|
|
17
|
+
"""
|
|
18
|
+
Initialize sandbox browser manager with Browserless support.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
sandbox: The sandbox instance (optional, kept for compatibility)
|
|
22
|
+
"""
|
|
23
|
+
# Initialize parent class with Browserless backend
|
|
24
|
+
super().__init__(browser_backend="browserless")
|
|
25
|
+
self.sandbox = sandbox
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Utility functions for event loop cleanup in task workers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import logging
|
|
7
|
+
|
|
8
|
+
logger = logging.getLogger(__name__)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def cleanup_event_loop(loop: asyncio.AbstractEventLoop) -> None:
|
|
12
|
+
"""Clean up a manually managed event loop."""
|
|
13
|
+
try:
|
|
14
|
+
tasks_to_cancel = asyncio.all_tasks(loop)
|
|
15
|
+
if tasks_to_cancel:
|
|
16
|
+
for task in tasks_to_cancel:
|
|
17
|
+
task.cancel()
|
|
18
|
+
try:
|
|
19
|
+
loop.run_until_complete(asyncio.gather(*tasks_to_cancel, return_exceptions=True))
|
|
20
|
+
except RuntimeError as error:
|
|
21
|
+
logger.warning("Could not wait for task cancellation: %s", error)
|
|
22
|
+
|
|
23
|
+
try:
|
|
24
|
+
loop.run_until_complete(loop.shutdown_asyncgens())
|
|
25
|
+
except RuntimeError as error:
|
|
26
|
+
logger.warning("Could not shut down async generators: %s", error)
|
|
27
|
+
|
|
28
|
+
try:
|
|
29
|
+
loop.run_until_complete(asyncio.sleep(0))
|
|
30
|
+
except RuntimeError as error:
|
|
31
|
+
logger.warning("Could not process scheduled callbacks: %s", error)
|
|
32
|
+
|
|
33
|
+
pending = asyncio.all_tasks(loop)
|
|
34
|
+
if pending:
|
|
35
|
+
try:
|
|
36
|
+
loop.run_until_complete(asyncio.gather(*pending, return_exceptions=True))
|
|
37
|
+
except RuntimeError as error:
|
|
38
|
+
logger.warning("Could not wait for remaining tasks: %s", error)
|
|
39
|
+
|
|
40
|
+
finally:
|
|
41
|
+
if not loop.is_closed():
|
|
42
|
+
loop.close()
|
|
43
|
+
|