openhands-agent-server 1.44.1__tar.gz → 1.45.0__tar.gz
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.
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/PKG-INFO +1 -1
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/api.py +3 -20
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/canvas_extensions/installed.py +2 -1
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/config.py +0 -4
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/conversation_service.py +54 -28
- openhands_agent_server-1.45.0/openhands/agent_server/desktop_router.py +28 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/docker/Dockerfile +122 -57
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/docker/build.py +93 -8
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/event_service.py +19 -4
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/persistence/store.py +9 -14
- openhands_agent_server-1.45.0/openhands/agent_server/session_protocol.py +156 -0
- openhands_agent_server-1.45.0/openhands/agent_server/session_socket.py +437 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/skills_router.py +0 -12
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands_agent_server.egg-info/PKG-INFO +1 -1
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands_agent_server.egg-info/SOURCES.txt +4 -4
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/pyproject.toml +1 -1
- openhands_agent_server-1.44.1/openhands/agent_server/desktop_router.py +0 -47
- openhands_agent_server-1.44.1/openhands/agent_server/desktop_service.py +0 -224
- openhands_agent_server-1.44.1/openhands/agent_server/docker/wallpaper.svg +0 -22
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/__init__.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/__main__.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/_secret_redaction.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/_secrets_exposure.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/agent_profiles_router.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/auth_router.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/bash_router.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/bash_service.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/canvas_extensions/__init__.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/canvas_extensions/manifest.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/canvas_extensions_router.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/conversation_lease.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/conversation_router.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/credential_binding.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/dependencies.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/env_parser.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/event_router.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/file_router.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/git_router.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/hooks_router.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/hooks_service.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/init_router.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/llm_router.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/logging_config.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/mcp_oauth_store.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/mcp_router.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/middleware.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/models.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/openai/__init__.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/openai/models.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/openai/router.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/openai/service.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/openapi.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/persistence/__init__.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/persistence/models.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/plugins_router.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/plugins_service.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/profiles_router.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/provider_connections_router.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/pub_sub.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/py.typed +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/server_details_router.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/settings_router.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/skills_service.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/sockets.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/sub_agents_router.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/telemetry/__init__.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/telemetry/factory.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/telemetry/http_exporter.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/telemetry/models.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/telemetry/policy.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/telemetry/posthog_exporter.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/telemetry/sanitizer.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/telemetry/service.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/telemetry/sink.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/telemetry/subscriber.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/telemetry_types.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/tool_preload_service.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/tool_router.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/utils.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/vscode_extensions/openhands-settings/extension.js +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/vscode_extensions/openhands-settings/package.json +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/vscode_router.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/vscode_service.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/workspace_router.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/workspaces_router.py +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands_agent_server.egg-info/dependency_links.txt +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands_agent_server.egg-info/entry_points.txt +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands_agent_server.egg-info/requires.txt +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands_agent_server.egg-info/top_level.txt +0 -0
- {openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: openhands-agent-server
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.45.0
|
|
4
4
|
Summary: OpenHands Agent Server - REST/WebSocket interface for OpenHands AI Agent
|
|
5
5
|
Project-URL: Source, https://github.com/OpenHands/software-agent-sdk
|
|
6
6
|
Project-URL: Homepage, https://github.com/OpenHands/software-agent-sdk
|
{openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/api.py
RENAMED
|
@@ -39,7 +39,6 @@ from openhands.agent_server.dependencies import (
|
|
|
39
39
|
check_workspace_session,
|
|
40
40
|
)
|
|
41
41
|
from openhands.agent_server.desktop_router import desktop_router
|
|
42
|
-
from openhands.agent_server.desktop_service import get_desktop_service
|
|
43
42
|
from openhands.agent_server.event_router import event_router
|
|
44
43
|
from openhands.agent_server.file_router import file_router
|
|
45
44
|
from openhands.agent_server.git_router import git_router
|
|
@@ -66,6 +65,7 @@ from openhands.agent_server.server_details_router import (
|
|
|
66
65
|
mark_initialization_complete,
|
|
67
66
|
server_details_router,
|
|
68
67
|
)
|
|
68
|
+
from openhands.agent_server.session_socket import session_router
|
|
69
69
|
from openhands.agent_server.settings_router import settings_router
|
|
70
70
|
from openhands.agent_server.skills_router import skills_router
|
|
71
71
|
from openhands.agent_server.sockets import sockets_router
|
|
@@ -167,7 +167,6 @@ async def api_lifespan(api: FastAPI) -> AsyncIterator[None]:
|
|
|
167
167
|
emit_server_started()
|
|
168
168
|
|
|
169
169
|
vscode_service = get_vscode_service()
|
|
170
|
-
desktop_service = get_desktop_service()
|
|
171
170
|
tool_preload_service = get_tool_preload_service()
|
|
172
171
|
|
|
173
172
|
# Define async functions for starting each service
|
|
@@ -183,18 +182,6 @@ async def api_lifespan(api: FastAPI) -> AsyncIterator[None]:
|
|
|
183
182
|
else:
|
|
184
183
|
logger.info("VSCode service is disabled")
|
|
185
184
|
|
|
186
|
-
async def start_desktop_service():
|
|
187
|
-
if desktop_service is not None:
|
|
188
|
-
desktop_started = await desktop_service.start()
|
|
189
|
-
if desktop_started:
|
|
190
|
-
logger.info("Desktop service started successfully")
|
|
191
|
-
else:
|
|
192
|
-
logger.warning(
|
|
193
|
-
"Desktop service failed to start, continuing without desktop"
|
|
194
|
-
)
|
|
195
|
-
else:
|
|
196
|
-
logger.info("Desktop service is disabled")
|
|
197
|
-
|
|
198
185
|
async def start_tool_preload_service():
|
|
199
186
|
if tool_preload_service is not None:
|
|
200
187
|
tool_preload_started = await tool_preload_service.start()
|
|
@@ -208,7 +195,6 @@ async def api_lifespan(api: FastAPI) -> AsyncIterator[None]:
|
|
|
208
195
|
# Start all services concurrently
|
|
209
196
|
results = await asyncio.gather(
|
|
210
197
|
start_vscode_service(),
|
|
211
|
-
start_desktop_service(),
|
|
212
198
|
start_tool_preload_service(),
|
|
213
199
|
return_exceptions=True,
|
|
214
200
|
)
|
|
@@ -231,17 +217,12 @@ async def api_lifespan(api: FastAPI) -> AsyncIterator[None]:
|
|
|
231
217
|
if vscode_service is not None:
|
|
232
218
|
await vscode_service.stop()
|
|
233
219
|
|
|
234
|
-
async def stop_desktop_service():
|
|
235
|
-
if desktop_service is not None:
|
|
236
|
-
await desktop_service.stop()
|
|
237
|
-
|
|
238
220
|
async def stop_tool_preload_service():
|
|
239
221
|
if tool_preload_service is not None:
|
|
240
222
|
await tool_preload_service.stop()
|
|
241
223
|
|
|
242
224
|
await asyncio.gather(
|
|
243
225
|
stop_vscode_service(),
|
|
244
|
-
stop_desktop_service(),
|
|
245
226
|
stop_tool_preload_service(),
|
|
246
227
|
return_exceptions=True,
|
|
247
228
|
)
|
|
@@ -471,6 +452,8 @@ def _add_api_routes(app: FastAPI) -> None:
|
|
|
471
452
|
|
|
472
453
|
app.include_router(sockets_router)
|
|
473
454
|
|
|
455
|
+
app.include_router(session_router)
|
|
456
|
+
|
|
474
457
|
|
|
475
458
|
def _setup_static_files(app: FastAPI, config: Config) -> None:
|
|
476
459
|
"""Set up static file serving and root redirect if configured.
|
|
@@ -35,6 +35,7 @@ from openhands.sdk.extensions.installation import (
|
|
|
35
35
|
InstallationMetadata,
|
|
36
36
|
)
|
|
37
37
|
from openhands.sdk.extensions.installation.manager import DEFAULT_CACHE_DIR
|
|
38
|
+
from openhands.sdk.utils.path import get_user_persistence_dir
|
|
38
39
|
|
|
39
40
|
|
|
40
41
|
# Matches the InstalledPluginInfo naming convention.
|
|
@@ -43,7 +44,7 @@ InstalledCanvasExtensionInfo = InstallationInfo
|
|
|
43
44
|
|
|
44
45
|
def get_installed_canvas_extensions_dir() -> Path:
|
|
45
46
|
"""Get the default directory for installed canvas extensions."""
|
|
46
|
-
return
|
|
47
|
+
return get_user_persistence_dir() / "canvas-extensions" / "installed"
|
|
47
48
|
|
|
48
49
|
|
|
49
50
|
class CanvasExtensionInstallationInterface(
|
{openhands_agent_server-1.44.1 → openhands_agent_server-1.45.0}/openhands/agent_server/config.py
RENAMED
|
@@ -319,10 +319,6 @@ class Config(BaseModel):
|
|
|
319
319
|
"For example, '/{runtime_id}/vscode' when using path-based routing."
|
|
320
320
|
),
|
|
321
321
|
)
|
|
322
|
-
enable_vnc: bool = Field(
|
|
323
|
-
default=False,
|
|
324
|
-
description="Whether to enable VNC desktop functionality",
|
|
325
|
-
)
|
|
326
322
|
preload_tools: bool = Field(
|
|
327
323
|
default=True,
|
|
328
324
|
description="Whether to preload tools",
|
|
@@ -130,6 +130,22 @@ def _append_system_message_suffix(agent: AgentBase, addition: str) -> AgentBase:
|
|
|
130
130
|
return agent.model_copy(update={"agent_context": updated_context})
|
|
131
131
|
|
|
132
132
|
|
|
133
|
+
def _with_load_memory(agent: AgentBase) -> AgentBase:
|
|
134
|
+
"""Stamp the global persistent-memory preference onto an agent.
|
|
135
|
+
|
|
136
|
+
``load_memory`` is a user-level setting, not part of any agent, profile or
|
|
137
|
+
client payload, so it is applied here regardless of how the agent reached
|
|
138
|
+
the request.
|
|
139
|
+
"""
|
|
140
|
+
# current_datetime stays suppressed on a synthesized context: a null
|
|
141
|
+
# agent_context means "no prompt context", and ACPAgent._render_suffix
|
|
142
|
+
# relies on that to keep a <CURRENT_DATETIME> block out of the prompt.
|
|
143
|
+
context = agent.agent_context or AgentContext(current_datetime=None)
|
|
144
|
+
return agent.model_copy(
|
|
145
|
+
update={"agent_context": context.model_copy(update={"load_memory": True})}
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
|
|
133
149
|
def _has_git_remote(repo_root: Path, remote: str = "origin") -> bool:
|
|
134
150
|
try:
|
|
135
151
|
run_git_command(["git", "remote", "get-url", remote], repo_root)
|
|
@@ -335,7 +351,6 @@ def _resolve_agent_from_profile(
|
|
|
335
351
|
profile_id: "UUID",
|
|
336
352
|
cipher: "Cipher | None",
|
|
337
353
|
mcp_config: "dict[str, MCPServer]",
|
|
338
|
-
load_memory: bool = False,
|
|
339
354
|
acp_skill_sourcing: ACPSkillSourcing = "native",
|
|
340
355
|
) -> "tuple[AgentBase, LaunchedAgentProfile]":
|
|
341
356
|
"""Load and resolve an agent profile by id, returning the built agent + provenance.
|
|
@@ -347,11 +362,6 @@ def _resolve_agent_from_profile(
|
|
|
347
362
|
server's cipher. Passed explicitly so this free function never
|
|
348
363
|
touches the settings-store singleton (which may not have been
|
|
349
364
|
initialised with the correct cipher yet).
|
|
350
|
-
load_memory: The user's global persistent-memory preference
|
|
351
|
-
(``agent_settings.agent_context.load_memory``). An ``AgentProfile``
|
|
352
|
-
has no ``agent_context`` field, so the preference cannot ride the
|
|
353
|
-
profile — it is stamped onto the resolved agent below, else a
|
|
354
|
-
profile-launched conversation would silently ignore the setting.
|
|
355
365
|
acp_skill_sourcing: This deployment's ACP skill policy
|
|
356
366
|
(``Config.acp_skill_sourcing``). Decides whether an ACP profile is
|
|
357
367
|
resolved with the server's managed skill catalog or with none.
|
|
@@ -435,15 +445,7 @@ def _resolve_agent_from_profile(
|
|
|
435
445
|
agent = agent.model_copy(
|
|
436
446
|
update={"tools": [*agent.tools, Tool(name=BROWSER_TOOL_NAME)]}
|
|
437
447
|
)
|
|
438
|
-
|
|
439
|
-
# is carried across the profile-resolution boundary the same way the global
|
|
440
|
-
# ``mcp_config`` is. Left untouched when off, so the resolved agent stays
|
|
441
|
-
# byte-identical for everyone who hasn't opted in.
|
|
442
|
-
if load_memory:
|
|
443
|
-
context = agent.agent_context or AgentContext()
|
|
444
|
-
agent = agent.model_copy(
|
|
445
|
-
update={"agent_context": context.model_copy(update={"load_memory": True})}
|
|
446
|
-
)
|
|
448
|
+
|
|
447
449
|
launched = LaunchedAgentProfile(
|
|
448
450
|
agent_profile_id=profile.id,
|
|
449
451
|
revision=profile.revision,
|
|
@@ -1561,32 +1563,56 @@ class ConversationService:
|
|
|
1561
1563
|
f"to a different workspace"
|
|
1562
1564
|
)
|
|
1563
1565
|
|
|
1564
|
-
# Profile resolution must happen before
|
|
1565
|
-
# asserts request.agent is not None)
|
|
1566
|
-
# agent is captured in request_data.
|
|
1566
|
+
# Profile resolution and the load_memory stamp must happen before
|
|
1567
|
+
# _prepare_request_workspace (which asserts request.agent is not None)
|
|
1568
|
+
# and before model_dump so the resolved agent is captured in request_data.
|
|
1567
1569
|
launched_agent_profile: LaunchedAgentProfile | None = None
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1570
|
+
|
|
1571
|
+
from openhands.agent_server.persistence import (
|
|
1572
|
+
PersistedSettings,
|
|
1573
|
+
get_settings_store,
|
|
1574
|
+
)
|
|
1575
|
+
|
|
1576
|
+
# get_settings_store() is safe here: get_instance() initialises the
|
|
1577
|
+
# singleton with the server cipher before any conversation can start.
|
|
1578
|
+
# FileSettingsStore.load re-raises PermissionError/OSError by design;
|
|
1579
|
+
# now that every launch reads it, a bad file mode must not take down
|
|
1580
|
+
# request shapes that need nothing from settings.
|
|
1581
|
+
try:
|
|
1582
|
+
settings = await asyncio.to_thread(
|
|
1583
|
+
lambda: get_settings_store().load() or PersistedSettings()
|
|
1584
|
+
)
|
|
1585
|
+
except (PermissionError, OSError):
|
|
1586
|
+
logger.warning(
|
|
1587
|
+
"Cannot read settings; starting without the stored agent preferences",
|
|
1588
|
+
exc_info=True,
|
|
1574
1589
|
)
|
|
1590
|
+
settings = PersistedSettings()
|
|
1575
1591
|
|
|
1576
|
-
|
|
1592
|
+
# ``ACPAgentSettings.agent_context`` is nullable, hence the guard.
|
|
1593
|
+
stored_context = settings.agent_settings.agent_context
|
|
1594
|
+
load_memory = bool(stored_context and stored_context.load_memory)
|
|
1595
|
+
|
|
1596
|
+
if request.agent_profile_id is not None:
|
|
1577
1597
|
mcp_config = settings.agent_settings.mcp_config
|
|
1578
|
-
# ``ACPAgentSettings.agent_context`` is nullable, hence the guard.
|
|
1579
|
-
stored_context = settings.agent_settings.agent_context
|
|
1580
1598
|
resolved_agent, launched_agent_profile = await asyncio.to_thread(
|
|
1581
1599
|
_resolve_agent_from_profile,
|
|
1582
1600
|
request.agent_profile_id,
|
|
1583
1601
|
self.cipher,
|
|
1584
1602
|
mcp_config,
|
|
1585
|
-
load_memory=bool(stored_context and stored_context.load_memory),
|
|
1586
1603
|
acp_skill_sourcing=self.acp_skill_sourcing,
|
|
1587
1604
|
)
|
|
1588
1605
|
request = request.model_copy(update={"agent": resolved_agent})
|
|
1589
1606
|
|
|
1607
|
+
# Applied unconditionally: a serialized agent always carries
|
|
1608
|
+
# ``load_memory`` (model_dump emits defaults), so there is no way to
|
|
1609
|
+
# tell a deliberate ``false`` from an echoed one. Opting a single
|
|
1610
|
+
# conversation out needs a tri-state field; tracked separately.
|
|
1611
|
+
if load_memory and request.agent is not None:
|
|
1612
|
+
request = request.model_copy(
|
|
1613
|
+
update={"agent": _with_load_memory(request.agent)}
|
|
1614
|
+
)
|
|
1615
|
+
|
|
1590
1616
|
request = request.model_copy(
|
|
1591
1617
|
update={
|
|
1592
1618
|
"agent": _apply_acp_skill_sourcing(
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"""Desktop router for agent server API endpoints."""
|
|
2
|
+
|
|
3
|
+
from fastapi import APIRouter, HTTPException
|
|
4
|
+
from pydantic import BaseModel
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
desktop_router = APIRouter(prefix="/desktop", tags=["Desktop"])
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class DesktopUrlResponse(BaseModel):
|
|
11
|
+
"""Response model for Desktop URL."""
|
|
12
|
+
|
|
13
|
+
url: str | None
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@desktop_router.get("/url", response_model=DesktopUrlResponse, deprecated=True)
|
|
17
|
+
async def get_desktop_url(
|
|
18
|
+
base_url: str = "http://localhost:8002", # noqa: ARG001
|
|
19
|
+
) -> DesktopUrlResponse:
|
|
20
|
+
"""Deprecated since v1.44.1 and scheduled for removal in v1.49.0.
|
|
21
|
+
|
|
22
|
+
The VNC/desktop stack has been removed; this always returns 503, the
|
|
23
|
+
same response every deployment has always gotten since VNC defaults off.
|
|
24
|
+
"""
|
|
25
|
+
raise HTTPException(
|
|
26
|
+
status_code=503,
|
|
27
|
+
detail="Desktop is disabled. VNC/desktop support has been removed.",
|
|
28
|
+
)
|
|
@@ -13,12 +13,18 @@ ARG PORT=8000
|
|
|
13
13
|
# to bundle google-cloud-aiplatform so the resulting binary supports
|
|
14
14
|
# `vertex_ai/*` partner models (MiniMax, Qwen, Kimi MaaS endpoints).
|
|
15
15
|
ARG ENABLE_VERTEX=0
|
|
16
|
-
# Comma-separated ACP provider keys (see
|
|
17
|
-
# openhands-sdk/openhands/sdk/settings/
|
|
16
|
+
# Comma-separated ACP provider keys (see ACP_INSTALL_CATALOG in
|
|
17
|
+
# openhands-sdk/openhands/sdk/settings/acp_install_catalog.py) to bake into the
|
|
18
18
|
# acp-providers stage below. The default reproduces the full provider set
|
|
19
|
-
# shipped today; an unknown key fails the
|
|
20
|
-
# installing nothing.
|
|
19
|
+
# shipped today (DEFAULT_PREINSTALLED_ACP_PROVIDERS); an unknown key fails the
|
|
20
|
+
# build rather than silently installing nothing.
|
|
21
21
|
ARG INSTALL_ACP_PROVIDERS=claude-code,codex,gemini-cli
|
|
22
|
+
# Comma-separated capability keys to include in the `base-image` stage below
|
|
23
|
+
# (`base-image-minimal` is unaffected). The default reproduces today's full
|
|
24
|
+
# contents; an unknown key fails the build. The GitHub CLI is expected to
|
|
25
|
+
# join this list later (see OpenHands/software-agent-sdk#4645) — the parsing
|
|
26
|
+
# below is written so that's a one-case addition, not a signature change.
|
|
27
|
+
ARG INSTALL_CAPABILITIES=vscode,browser,docker
|
|
22
28
|
|
|
23
29
|
####################################################################################
|
|
24
30
|
# Builder (source mode)
|
|
@@ -96,6 +102,11 @@ RUN test -x /agent-server/dist/openhands-agent-server
|
|
|
96
102
|
# Failures here are fatal: there is no per-BASE_IMAGE fallback for a broken build of
|
|
97
103
|
# the shared payload itself (see the compatibility probe in base-image-minimal for
|
|
98
104
|
# the per-target-image fallback).
|
|
105
|
+
#
|
|
106
|
+
# The Node pin below is a floor, not a preference: this stage installs every
|
|
107
|
+
# selected provider under one runtime, so it has to clear the highest
|
|
108
|
+
# `min_node_version` in ACP_INSTALL_CATALOG (asserted by
|
|
109
|
+
# tests/cross/test_agent_server_build_metadata.py).
|
|
99
110
|
####################################################################################
|
|
100
111
|
FROM python:3.13-bookworm AS acp-providers
|
|
101
112
|
ARG INSTALL_ACP_PROVIDERS
|
|
@@ -106,23 +117,17 @@ ARG INSTALL_ACP_PROVIDERS
|
|
|
106
117
|
# parent, mirroring /agent-server's own top-level placement in the builder
|
|
107
118
|
# stage above.
|
|
108
119
|
ENV ACP_NODE_DIR=/acp-node
|
|
120
|
+
# The catalog module is dependency-free (stdlib only), so it can be COPYed
|
|
121
|
+
# into this bare `python` image and run as-is — no pip install, and no need
|
|
122
|
+
# to pull in the rest of the SDK source (which would break the zero-context
|
|
123
|
+
# fast path `build.py` uses for the `base-image-minimal` target). Adding an
|
|
124
|
+
# npm provider is a catalog edit only; this stage never needs to change.
|
|
125
|
+
COPY openhands-sdk/openhands/sdk/settings/acp_install_catalog.py /tmp/acp_install_catalog.py
|
|
109
126
|
RUN set -eux; \
|
|
110
127
|
ACP_WRAPPER_DIR=/opt/acp-wrappers; \
|
|
111
128
|
mkdir -p "$ACP_NODE_DIR" "$ACP_WRAPPER_DIR"; \
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
for provider in $(echo "$INSTALL_ACP_PROVIDERS" | tr ',' ' '); do \
|
|
115
|
-
case "$provider" in \
|
|
116
|
-
claude-code) PACKAGES="$PACKAGES @agentclientprotocol/claude-agent-acp@0.63.0"; \
|
|
117
|
-
WRAPPER_BINS="$WRAPPER_BINS claude-agent-acp";; \
|
|
118
|
-
codex) PACKAGES="$PACKAGES @agentclientprotocol/codex-acp@1.1.7"; \
|
|
119
|
-
WRAPPER_BINS="$WRAPPER_BINS codex-acp";; \
|
|
120
|
-
gemini-cli) PACKAGES="$PACKAGES @google/gemini-cli@0.46.0"; \
|
|
121
|
-
WRAPPER_BINS="$WRAPPER_BINS gemini";; \
|
|
122
|
-
*) echo "Unknown ACP provider '$provider' in INSTALL_ACP_PROVIDERS" \
|
|
123
|
-
"(expected one of: claude-code, codex, gemini-cli)" >&2; exit 1;; \
|
|
124
|
-
esac; \
|
|
125
|
-
done; \
|
|
129
|
+
PLAN=$(python3 /tmp/acp_install_catalog.py "$INSTALL_ACP_PROVIDERS") || exit 1; \
|
|
130
|
+
eval "$PLAN"; \
|
|
126
131
|
if [ -z "$PACKAGES" ]; then \
|
|
127
132
|
echo "INSTALL_ACP_PROVIDERS is empty; no ACP providers will be installed"; \
|
|
128
133
|
exit 0; \
|
|
@@ -133,29 +138,38 @@ RUN set -eux; \
|
|
|
133
138
|
rm -rf /var/lib/apt/lists/*; \
|
|
134
139
|
ARCH=$(uname -m); \
|
|
135
140
|
case "$ARCH" in \
|
|
136
|
-
x86_64|amd64) NARCH=x64; NODE_SHA256=
|
|
137
|
-
aarch64|arm64) NARCH=arm64; NODE_SHA256=
|
|
141
|
+
x86_64|amd64) NARCH=x64; NODE_SHA256=c0649af18e6a24f6fe5535a3e86b341dd49a8e71117c8b68bde973ef834f16f2;; \
|
|
142
|
+
aarch64|arm64) NARCH=arm64; NODE_SHA256=0b2d9f564b6594222a62c82e1df2efe119dd4a4aff29644f4dd325bf360b6bcc;; \
|
|
138
143
|
*) echo "Unsupported architecture for ACP providers: '$ARCH'" >&2; exit 1;; \
|
|
139
144
|
esac; \
|
|
140
|
-
NODE_TARBALL="/tmp/node-v22.
|
|
145
|
+
NODE_TARBALL="/tmp/node-v22.19.0-linux-${NARCH}.tar.xz"; \
|
|
141
146
|
curl -fsSL --retry 5 --retry-delay 2 --retry-connrefused \
|
|
142
|
-
"https://nodejs.org/dist/v22.
|
|
147
|
+
"https://nodejs.org/dist/v22.19.0/node-v22.19.0-linux-${NARCH}.tar.xz" -o "$NODE_TARBALL"; \
|
|
143
148
|
echo "$NODE_SHA256 $NODE_TARBALL" | sha256sum -c -; \
|
|
144
149
|
tar -xJ --strip-components=1 -C "$ACP_NODE_DIR" -f "$NODE_TARBALL"; \
|
|
145
150
|
rm -f "$NODE_TARBALL"; \
|
|
146
151
|
"$ACP_NODE_DIR/bin/node" --version; \
|
|
147
152
|
PATH="$ACP_NODE_DIR/bin:$PATH" "$ACP_NODE_DIR/bin/npm" install -g $PACKAGES; \
|
|
148
153
|
for bin in $WRAPPER_BINS; do \
|
|
154
|
+
# Fail the build rather than ship a wrapper that execs a missing target:
|
|
155
|
+
# a wrong binary_name is otherwise invisible until someone launches the
|
|
156
|
+
# provider, since the wrapper is only a shell script we chmod +x.
|
|
157
|
+
[ -x "$ACP_NODE_DIR/bin/$bin" ] || { \
|
|
158
|
+
echo "ACP wrapper target not installed: $ACP_NODE_DIR/bin/$bin" >&2; \
|
|
159
|
+
echo "(check binary_name in acp_install_catalog.py against the package's npm bin)" >&2; \
|
|
160
|
+
exit 1; \
|
|
161
|
+
}; \
|
|
149
162
|
printf '#!/bin/sh\nPATH="%s/bin:$PATH" exec "%s/bin/%s" "$@"\n' \
|
|
150
163
|
"$ACP_NODE_DIR" "$ACP_NODE_DIR" "$bin" \
|
|
151
164
|
> "$ACP_WRAPPER_DIR/$bin"; \
|
|
152
165
|
chmod +x "$ACP_WRAPPER_DIR/$bin"; \
|
|
153
|
-
done
|
|
166
|
+
done; \
|
|
167
|
+
printf '%s\n' $WRAPPER_BINS > "$ACP_NODE_DIR/acp-wrappers.txt"
|
|
154
168
|
|
|
155
169
|
####################################################################################
|
|
156
170
|
# Base image (minimal)
|
|
157
171
|
# It includes only basic packages and the UV runtime.
|
|
158
|
-
# No Docker, no
|
|
172
|
+
# No Docker, no browser, no VSCode Web.
|
|
159
173
|
# Suitable for running in headless/evaluation mode.
|
|
160
174
|
####################################################################################
|
|
161
175
|
FROM ${BASE_IMAGE} AS base-image-minimal
|
|
@@ -224,7 +238,7 @@ RUN set -eux; \
|
|
|
224
238
|
mkdir -p /workspace/project; \
|
|
225
239
|
chown -R "${USERNAME}:${USERNAME}" /workspace
|
|
226
240
|
|
|
227
|
-
# Install ACP servers
|
|
241
|
+
# Install the ACP servers selected by INSTALL_ACP_PROVIDERS (see the stage above).
|
|
228
242
|
# The payload itself (Node.js 22 + the ACP npm packages, under $ACP_NODE_DIR) is
|
|
229
243
|
# built once in the acp-providers stage above, independent of BASE_IMAGE, and
|
|
230
244
|
# copied in here so the layer is byte-identical across every base image it lands
|
|
@@ -243,11 +257,16 @@ COPY --from=acp-providers /opt/acp-wrappers/. /usr/local/bin/
|
|
|
243
257
|
# glibc-modern stage, so a failure here means "incompatible with this specific
|
|
244
258
|
# BASE_IMAGE", not "the ACP install failed". When that happens we remove the
|
|
245
259
|
# copied payload and wrappers so the rest of the build (and non-ACP agents)
|
|
246
|
-
# still work, rather than leaving binaries in place that cannot execute.
|
|
260
|
+
# still work, rather than leaving binaries in place that cannot execute. The
|
|
261
|
+
# wrapper names come from the manifest the acp-providers stage wrote, so this
|
|
262
|
+
# tracks INSTALL_ACP_PROVIDERS instead of a list that goes stale per provider.
|
|
247
263
|
RUN if ! "$ACP_NODE_DIR/bin/node" --version >/dev/null 2>&1; then \
|
|
248
264
|
echo "Warning: ACP Node 22 runtime is not compatible with this base image (likely older glibc or musl libc); ACP agents will not be available" >&2; \
|
|
265
|
+
if [ -f "$ACP_NODE_DIR/acp-wrappers.txt" ]; then \
|
|
266
|
+
while read -r bin; do rm -f "/usr/local/bin/$bin"; done \
|
|
267
|
+
< "$ACP_NODE_DIR/acp-wrappers.txt"; \
|
|
268
|
+
fi; \
|
|
249
269
|
rm -rf "$ACP_NODE_DIR"/*; \
|
|
250
|
-
rm -f /usr/local/bin/claude-agent-acp /usr/local/bin/codex-acp /usr/local/bin/gemini; \
|
|
251
270
|
fi
|
|
252
271
|
|
|
253
272
|
# Configure Claude Code managed settings for headless operation:
|
|
@@ -264,7 +283,6 @@ WORKDIR /
|
|
|
264
283
|
# Locale settings required for libtmux to work with PyInstaller builds
|
|
265
284
|
ENV LC_ALL=C.UTF-8
|
|
266
285
|
ENV LANG=C.UTF-8
|
|
267
|
-
ENV OH_ENABLE_VNC=false
|
|
268
286
|
ENV LOG_JSON=true
|
|
269
287
|
# The ACP CLIs run without the user's host home here, so OpenHands supplies the
|
|
270
288
|
# managed skill catalog they cannot read for themselves (#4019).
|
|
@@ -273,20 +291,49 @@ EXPOSE ${PORT}
|
|
|
273
291
|
|
|
274
292
|
####################################################################################
|
|
275
293
|
# Base image (full)
|
|
276
|
-
# It includes additional Docker,
|
|
294
|
+
# It includes additional Docker, browser, and VSCode Web.
|
|
277
295
|
####################################################################################
|
|
278
296
|
FROM base-image-minimal AS base-image
|
|
279
297
|
ARG USERNAME
|
|
298
|
+
ARG INSTALL_CAPABILITIES
|
|
280
299
|
|
|
281
300
|
USER root
|
|
301
|
+
|
|
302
|
+
# Fail fast on a typo'd capability name before spending any time on the
|
|
303
|
+
# installs below (each block only checks for ITS OWN keyword's presence, so
|
|
304
|
+
# an unrecognized one would otherwise be silently ignored rather than erroring).
|
|
305
|
+
RUN set -eux; \
|
|
306
|
+
for capability in $(echo "$INSTALL_CAPABILITIES" | tr ',' ' '); do \
|
|
307
|
+
case "$capability" in \
|
|
308
|
+
vscode|browser|docker) ;; \
|
|
309
|
+
*) echo "Unknown capability '$capability' in INSTALL_CAPABILITIES" \
|
|
310
|
+
"(expected one of: vscode, browser, docker)" >&2; exit 1;; \
|
|
311
|
+
esac; \
|
|
312
|
+
done
|
|
313
|
+
|
|
282
314
|
# --- VSCode Web ---
|
|
315
|
+
# EDITOR/VISUAL/GIT_EDITOR/OPENVSCODE_SERVER_ROOT are declared unconditionally:
|
|
316
|
+
# nothing execs `code` unless VSCode Web itself is running, so a dangling
|
|
317
|
+
# path when `vscode` is excluded is harmless (same reasoning as CHROME_BIN
|
|
318
|
+
# below, applied to VSCode).
|
|
283
319
|
ENV EDITOR=code \
|
|
284
320
|
VISUAL=code \
|
|
285
321
|
GIT_EDITOR="code --wait" \
|
|
286
322
|
OPENVSCODE_SERVER_ROOT=/openhands/.openvscode-server
|
|
287
323
|
ARG RELEASE_TAG="openvscode-server-v1.98.2"
|
|
288
324
|
ARG RELEASE_ORG="gitpod-io"
|
|
325
|
+
# The --mount=from=builder on the extensions RUN below is unconditional at
|
|
326
|
+
# the Dockerfile level, so BuildKit still builds the full `builder` stage
|
|
327
|
+
# (the entire SDK venv) even when `vscode` is excluded from
|
|
328
|
+
# INSTALL_CAPABILITIES — the case check inside each RUN only decides what
|
|
329
|
+
# lands in THIS image, not what gets built as a dependency. Excluding vscode
|
|
330
|
+
# shrinks the result, not the build time.
|
|
289
331
|
RUN set -eux; \
|
|
332
|
+
case ",$INSTALL_CAPABILITIES," in \
|
|
333
|
+
*,vscode,*) ;; \
|
|
334
|
+
*) echo "INSTALL_CAPABILITIES excludes vscode; skipping OpenVSCode Server"; exit 0;; \
|
|
335
|
+
esac; \
|
|
336
|
+
\
|
|
290
337
|
# Create necessary directories
|
|
291
338
|
mkdir -p $(dirname ${OPENVSCODE_SERVER_ROOT}); \
|
|
292
339
|
\
|
|
@@ -311,13 +358,31 @@ RUN set -eux; \
|
|
|
311
358
|
# Set proper ownership
|
|
312
359
|
chown -R ${USERNAME}:${USERNAME} ${OPENVSCODE_SERVER_ROOT}
|
|
313
360
|
|
|
314
|
-
|
|
315
|
-
#
|
|
316
|
-
#
|
|
317
|
-
|
|
361
|
+
# Kept as its own RUN, separate from the tarball download above: extensions
|
|
362
|
+
# are actively-edited local source (unlike the pinned release tarball), so
|
|
363
|
+
# splitting them keeps an extension-only change from invalidating the ~100MB
|
|
364
|
+
# download's cache layer.
|
|
365
|
+
RUN --mount=type=bind,from=builder,source=/agent-server/openhands-agent-server/openhands/agent_server/vscode_extensions,target=/tmp/vscode_extensions \
|
|
366
|
+
set -eux; \
|
|
367
|
+
case ",$INSTALL_CAPABILITIES," in \
|
|
368
|
+
*,vscode,*) ;; \
|
|
369
|
+
*) exit 0;; \
|
|
370
|
+
esac; \
|
|
371
|
+
\
|
|
372
|
+
# Trailing slashes matter: OpenVSCode Server's tarball already ships its
|
|
373
|
+
# own extensions/ dir, so a bare `cp -r src dst` would nest src under the
|
|
374
|
+
# existing dst instead of merging into it the way `COPY` does.
|
|
375
|
+
mkdir -p ${OPENVSCODE_SERVER_ROOT}/extensions; \
|
|
376
|
+
cp -r /tmp/vscode_extensions/. ${OPENVSCODE_SERVER_ROOT}/extensions/; \
|
|
377
|
+
chown -R ${USERNAME}:${USERNAME} ${OPENVSCODE_SERVER_ROOT}/extensions
|
|
318
378
|
|
|
319
379
|
# --- Docker ---
|
|
320
380
|
RUN set -eux; \
|
|
381
|
+
case ",$INSTALL_CAPABILITIES," in \
|
|
382
|
+
*,docker,*) ;; \
|
|
383
|
+
*) echo "INSTALL_CAPABILITIES excludes docker; skipping Docker Engine"; exit 0;; \
|
|
384
|
+
esac; \
|
|
385
|
+
\
|
|
321
386
|
# Determine OS type and install Docker accordingly
|
|
322
387
|
if grep -q "ubuntu" /etc/os-release; then \
|
|
323
388
|
# Handle Ubuntu
|
|
@@ -336,10 +401,10 @@ RUN set -eux; \
|
|
|
336
401
|
apt-get update; \
|
|
337
402
|
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin; \
|
|
338
403
|
apt-get clean; \
|
|
339
|
-
rm -rf /var/lib/apt/lists
|
|
340
|
-
|
|
341
|
-
# Configure Docker daemon with MTU 1450 to prevent packet fragmentation issues
|
|
342
|
-
|
|
404
|
+
rm -rf /var/lib/apt/lists/*; \
|
|
405
|
+
\
|
|
406
|
+
# Configure Docker daemon with MTU 1450 to prevent packet fragmentation issues
|
|
407
|
+
mkdir -p /etc/docker; \
|
|
343
408
|
echo '{"mtu": 1450}' > /etc/docker/daemon.json
|
|
344
409
|
|
|
345
410
|
# --- GitHub CLI ---
|
|
@@ -355,36 +420,36 @@ RUN set -eux; \
|
|
|
355
420
|
apt-get clean; \
|
|
356
421
|
rm -rf /var/lib/apt/lists/*
|
|
357
422
|
|
|
358
|
-
# ---
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
apt-get install -y --no-install-recommends \
|
|
362
|
-
# GUI bits (remove entirely if headless)
|
|
363
|
-
tigervnc-standalone-server xfce4 dbus-x11 novnc websockify \
|
|
364
|
-
# Browser
|
|
365
|
-
$(if grep -q "ubuntu" /etc/os-release; then echo "chromium-browser"; else echo "chromium"; fi); \
|
|
366
|
-
apt-get clean; rm -rf /var/lib/apt/lists/*
|
|
367
|
-
|
|
368
|
-
ENV NOVNC_WEB=/usr/share/novnc \
|
|
369
|
-
NOVNC_PORT=8002 \
|
|
370
|
-
DISPLAY=:1 \
|
|
371
|
-
VNC_GEOMETRY=1280x800 \
|
|
372
|
-
CHROME_BIN=/usr/bin/chromium \
|
|
423
|
+
# --- Browser ---
|
|
424
|
+
# Chromium for BrowserToolSet. Selected by the `browser` capability.
|
|
425
|
+
ENV CHROME_BIN=/usr/bin/chromium \
|
|
373
426
|
PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium \
|
|
374
427
|
CHROMIUM_FLAGS="--no-sandbox --disable-dev-shm-usage --disable-gpu"
|
|
375
428
|
|
|
376
|
-
|
|
377
|
-
#
|
|
378
|
-
|
|
429
|
+
# CHROME_BIN/PUPPETEER_EXECUTABLE_PATH above are declared unconditionally
|
|
430
|
+
# regardless of which capabilities are selected. Nothing in this repo trusts
|
|
431
|
+
# them as an availability signal — browser_use's chromium detection
|
|
432
|
+
# (openhands-tools/openhands/tools/browser_use/impl.py) does its own filesystem
|
|
433
|
+
# probe of standard install paths — so when `browser` is excluded these simply
|
|
434
|
+
# point at a path that doesn't exist, rather than causing a false "available"
|
|
435
|
+
# signal downstream.
|
|
436
|
+
RUN set -eux; \
|
|
437
|
+
case ",$INSTALL_CAPABILITIES," in \
|
|
438
|
+
*,browser,*) \
|
|
439
|
+
apt-get update; \
|
|
440
|
+
apt-get install -y --no-install-recommends \
|
|
441
|
+
"$(if grep -q "ubuntu" /etc/os-release; then echo "chromium-browser"; else echo "chromium"; fi)"; \
|
|
442
|
+
apt-get clean; rm -rf /var/lib/apt/lists/*; \
|
|
443
|
+
;; \
|
|
444
|
+
esac
|
|
379
445
|
|
|
380
446
|
USER ${USERNAME}
|
|
381
447
|
WORKDIR /
|
|
382
|
-
ENV OH_ENABLE_VNC=false
|
|
383
448
|
ENV LOG_JSON=true
|
|
384
449
|
# The ACP CLIs run without the user's host home here, so OpenHands supplies the
|
|
385
450
|
# managed skill catalog they cannot read for themselves (#4019).
|
|
386
451
|
ENV OH_ACP_SKILL_SOURCING=openhands_managed
|
|
387
|
-
EXPOSE ${PORT}
|
|
452
|
+
EXPOSE ${PORT}
|
|
388
453
|
|
|
389
454
|
|
|
390
455
|
####################################################################################
|