agentenv-framework 0.9.1254__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.
- agent_env/__init__.py +0 -0
- agent_env/a2a_agent/__init__.py +23 -0
- agent_env/a2a_agent/a2a_agent.py +600 -0
- agent_env/a2a_agent/conversation_store.py +356 -0
- agent_env/a2a_agent/object_transfer.py +609 -0
- agent_env/a2a_agent/protocol.py +238 -0
- agent_env/a2a_agent/store.py +201 -0
- agent_env/a2a_agent/validator.py +901 -0
- agent_env/artifact/__init__.py +48 -0
- agent_env/artifact/artifact.py +74 -0
- agent_env/artifact/artifacts/__init__.py +0 -0
- agent_env/artifact/artifacts/cli.py +83 -0
- agent_env/artifact/artifacts/docker_image.py +457 -0
- agent_env/artifact/artifacts/environment.py +89 -0
- agent_env/artifact/artifacts/environment_universe.py +156 -0
- agent_env/artifact/artifacts/file.py +214 -0
- agent_env/artifact/artifacts/file_artifact_universe.py +206 -0
- agent_env/artifact/artifacts/skill.py +206 -0
- agent_env/artifact/artifacts/vm_image.py +95 -0
- agent_env/artifact/ref.py +15 -0
- agent_env/artifact/registry.py +251 -0
- agent_env/artifact/store.py +295 -0
- agent_env/artifact/universe.py +38 -0
- agent_env/attribution.py +41 -0
- agent_env/bundle/__init__.py +4 -0
- agent_env/bundle/_fs.py +128 -0
- agent_env/bundle/authoring.py +239 -0
- agent_env/bundle/installed.py +175 -0
- agent_env/bundle/ledger.py +269 -0
- agent_env/bundle/materialize.py +204 -0
- agent_env/bundle/parse.py +594 -0
- agent_env/bundle/plan.py +414 -0
- agent_env/bundle/resolve.py +404 -0
- agent_env/bundle/run.py +361 -0
- agent_env/cli/__init__.py +69 -0
- agent_env/cli/__main__.py +10 -0
- agent_env/cli/_installers.py +576 -0
- agent_env/cli/_plugin_changes.py +947 -0
- agent_env/cli/a2a_agent/__init__.py +22 -0
- agent_env/cli/a2a_agent/add_skill.py +97 -0
- agent_env/cli/a2a_agent/deploy.py +60 -0
- agent_env/cli/a2a_agent/get.py +15 -0
- agent_env/cli/a2a_agent/get_instance.py +26 -0
- agent_env/cli/a2a_agent/put.py +78 -0
- agent_env/cli/a2a_agent/validate.py +56 -0
- agent_env/cli/artifact/__init__.py +20 -0
- agent_env/cli/artifact/cli.py +83 -0
- agent_env/cli/artifact/environment.py +44 -0
- agent_env/cli/artifact/environment_universe.py +163 -0
- agent_env/cli/artifact/file_artifact_universe.py +281 -0
- agent_env/cli/artifact/skill.py +128 -0
- agent_env/cli/banner.py +25 -0
- agent_env/cli/config.py +263 -0
- agent_env/cli/env/__init__.py +31 -0
- agent_env/cli/env/deploy.py +112 -0
- agent_env/cli/env/gateway.py +57 -0
- agent_env/cli/env/get_instance.py +40 -0
- agent_env/cli/env/mcp_server.py +410 -0
- agent_env/cli/env/multi.py +282 -0
- agent_env/cli/env/service_db.py +99 -0
- agent_env/cli/env/snapshot.py +35 -0
- agent_env/cli/env/state/__init__.py +6 -0
- agent_env/cli/env/state/init.py +108 -0
- agent_env/cli/env/state/teardown.py +44 -0
- agent_env/cli/env/website.py +216 -0
- agent_env/cli/env/website_browser.py +67 -0
- agent_env/cli/eval/__init__.py +16 -0
- agent_env/cli/eval/add_tasks.py +63 -0
- agent_env/cli/eval/create.py +49 -0
- agent_env/cli/eval/run.py +194 -0
- agent_env/cli/identity/__init__.py +5 -0
- agent_env/cli/identity/client_identity.py +37 -0
- agent_env/cli/plugin.py +438 -0
- agent_env/cli/run.py +285 -0
- agent_env/cli/task/__init__.py +21 -0
- agent_env/cli/task/create.py +109 -0
- agent_env/cli/task/get.py +17 -0
- agent_env/cli/task/get_instance.py +34 -0
- agent_env/cli/task/run.py +615 -0
- agent_env/cli/task/validate.py +31 -0
- agent_env/cli/up.py +123 -0
- agent_env/cli/utils.py +183 -0
- agent_env/config/__init__.py +50 -0
- agent_env/config/describe.py +1067 -0
- agent_env/config/errors.py +13 -0
- agent_env/config/loader.py +245 -0
- agent_env/config/model.py +154 -0
- agent_env/config/paths.py +25 -0
- agent_env/config/plugin_tables.py +101 -0
- agent_env/config/provenance.py +134 -0
- agent_env/config/runtime.py +1064 -0
- agent_env/config/snapshot.py +81 -0
- agent_env/entity_refs.py +186 -0
- agent_env/env/__init__.py +49 -0
- agent_env/env/env.py +433 -0
- agent_env/env/env_artifact_store.py +87 -0
- agent_env/env/envs/__init__.py +7 -0
- agent_env/env/envs/_deployment.py +281 -0
- agent_env/env/envs/gateway_server.py +32 -0
- agent_env/env/envs/mcp_server.py +387 -0
- agent_env/env/envs/multi_env.py +780 -0
- agent_env/env/envs/service_db/Dockerfile +20 -0
- agent_env/env/envs/service_db/Dockerfile.db-mcp +42 -0
- agent_env/env/envs/service_db/Dockerfile.db-web +8 -0
- agent_env/env/envs/service_db.py +92 -0
- agent_env/env/envs/website.py +294 -0
- agent_env/env/envs/website_browser/Dockerfile +40 -0
- agent_env/env/envs/website_browser/__init__.py +17 -0
- agent_env/env/envs/website_browser/entrypoint.sh +13 -0
- agent_env/env/gateway/Dockerfile +14 -0
- agent_env/env/gateway/__init__.py +37 -0
- agent_env/env/gateway/clock.py +184 -0
- agent_env/env/gateway/constants.py +190 -0
- agent_env/env/gateway/entrypoint.py +156 -0
- agent_env/env/gateway/gateway.py +1095 -0
- agent_env/env/gateway/get_time.py +60 -0
- agent_env/env/gateway/requirements.txt +4 -0
- agent_env/env/gateway/triggers.py +1193 -0
- agent_env/env/legacy_protocol.py +146 -0
- agent_env/env/registry.py +93 -0
- agent_env/env/snapshot_store.py +372 -0
- agent_env/env/store.py +411 -0
- agent_env/eval/__init__.py +14 -0
- agent_env/eval/eval.py +68 -0
- agent_env/eval/store.py +116 -0
- agent_env/examples/hello/README.md +9 -0
- agent_env/examples/hello/artifacts/greeting/check.sh +1 -0
- agent_env/examples/hello/artifacts/greeting/hello.txt +1 -0
- agent_env/examples/hello/tasks/hello.json +10 -0
- agent_env/explorer/__init__.py +12 -0
- agent_env/explorer/app.py +277 -0
- agent_env/explorer/openapi_docs.py +242 -0
- agent_env/explorer/plugin.py +95 -0
- agent_env/explorer/routers/__init__.py +1 -0
- agent_env/explorer/routers/common.py +174 -0
- agent_env/explorer/routers/conversations.py +25 -0
- agent_env/explorer/routers/objects.py +112 -0
- agent_env/explorer/routers/runs.py +475 -0
- agent_env/plugins/__init__.py +59 -0
- agent_env/plugins/_cli.py +244 -0
- agent_env/plugins/_discovery.py +97 -0
- agent_env/plugins/_inventory.py +262 -0
- agent_env/plugins/_registration.py +268 -0
- agent_env/plugins/_report.py +47 -0
- agent_env/plugins/_requirements.py +55 -0
- agent_env/providers/__init__.py +37 -0
- agent_env/providers/env_providers/__init__.py +12 -0
- agent_env/providers/env_providers/constants.py +13 -0
- agent_env/providers/env_providers/env_gateway_provider.py +1354 -0
- agent_env/providers/env_providers/env_provider.py +237 -0
- agent_env/providers/env_providers/env_server_provider.py +133 -0
- agent_env/providers/env_state/__init__.py +49 -0
- agent_env/providers/env_state/env_state_provider.py +554 -0
- agent_env/providers/env_state/local_postgres.py +390 -0
- agent_env/providers/env_state/store.py +112 -0
- agent_env/providers/sandbox_providers/__init__.py +29 -0
- agent_env/providers/sandbox_providers/chained_sandbox_provider.py +93 -0
- agent_env/providers/sandbox_providers/e2b/__init__.py +6 -0
- agent_env/providers/sandbox_providers/e2b/provider.py +376 -0
- agent_env/providers/sandbox_providers/e2b/sandbox.py +342 -0
- agent_env/providers/sandbox_providers/e2b/template.py +158 -0
- agent_env/providers/sandbox_providers/local_sandbox.py +408 -0
- agent_env/providers/sandbox_providers/modal_sandbox.py +500 -0
- agent_env/providers/sandbox_providers/modal_vm_sandbox.py +466 -0
- agent_env/providers/sandbox_providers/sandbox.py +387 -0
- agent_env/providers/sandbox_providers/sandbox_provider.py +614 -0
- agent_env/py.typed +0 -0
- agent_env/runner/__init__.py +16 -0
- agent_env/runner/local_runner.py +190 -0
- agent_env/runner/runner.py +127 -0
- agent_env/runner/store.py +94 -0
- agent_env/store/__init__.py +126 -0
- agent_env/store/_google.py +68 -0
- agent_env/store/base.py +33 -0
- agent_env/store/document_store/__init__.py +60 -0
- agent_env/store/document_store/document_store.py +519 -0
- agent_env/store/document_store/dynamodb_document_store.py +306 -0
- agent_env/store/document_store/evaluation.py +199 -0
- agent_env/store/document_store/firestore_mongo_document_store.py +242 -0
- agent_env/store/document_store/mongo_document_store.py +259 -0
- agent_env/store/document_store/sqlite_document_store.py +314 -0
- agent_env/store/ids.py +123 -0
- agent_env/store/image_store/__init__.py +34 -0
- agent_env/store/image_store/ecr_image_store.py +131 -0
- agent_env/store/image_store/google_credentials.py +113 -0
- agent_env/store/image_store/image_store.py +113 -0
- agent_env/store/image_store/local_registry_image_store.py +106 -0
- agent_env/store/image_store/oci_registry_credentials.py +196 -0
- agent_env/store/local_state.py +27 -0
- agent_env/store/object_store/__init__.py +24 -0
- agent_env/store/object_store/gcs_object_store.py +415 -0
- agent_env/store/object_store/local_object_store.py +143 -0
- agent_env/store/object_store/object_store.py +198 -0
- agent_env/store/object_store/s3_object_store.py +354 -0
- agent_env/store/query.py +112 -0
- agent_env/store/routing.py +609 -0
- agent_env/store/secret_store/__init__.py +19 -0
- agent_env/store/secret_store/aws_secrets_manager_secret_store.py +293 -0
- agent_env/store/secret_store/gcp_secret_manager_secret_store.py +319 -0
- agent_env/store/secret_store/local_secret_store.py +47 -0
- agent_env/store/secret_store/secret_store.py +19 -0
- agent_env/task/__init__.py +27 -0
- agent_env/task/interrupts.py +109 -0
- agent_env/task/registry.py +27 -0
- agent_env/task/step_journal.py +99 -0
- agent_env/task/store.py +940 -0
- agent_env/task/task.py +815 -0
- agent_env/task/teardown.py +142 -0
- agent_env/task_step/__init__.py +47 -0
- agent_env/task_step/context.py +195 -0
- agent_env/task_step/context_ops.py +224 -0
- agent_env/task_step/registry.py +182 -0
- agent_env/task_step/review_store.py +118 -0
- agent_env/task_step/snapshot_utils/__init__.py +2 -0
- agent_env/task_step/snapshot_utils/agent_state_capture.py +261 -0
- agent_env/task_step/snapshot_utils/snapshot_series.py +631 -0
- agent_env/task_step/store.py +129 -0
- agent_env/task_step/task_step.py +173 -0
- agent_env/task_step/task_steps/__init__.py +27 -0
- agent_env/task_step/task_steps/a2a_agent_validator/__init__.py +31 -0
- agent_env/task_step/task_steps/a2a_agent_validator/fixtures/README.md +158 -0
- agent_env/task_step/task_steps/a2a_agent_validator/fixtures/clip.m4a +0 -0
- agent_env/task_step/task_steps/a2a_agent_validator/fixtures/clip.mp3 +0 -0
- agent_env/task_step/task_steps/a2a_agent_validator/fixtures/clip.mp4 +0 -0
- agent_env/task_step/task_steps/a2a_agent_validator/fixtures/clip.ogg +0 -0
- agent_env/task_step/task_steps/a2a_agent_validator/fixtures/clip.wav +0 -0
- agent_env/task_step/task_steps/a2a_agent_validator/fixtures/document.pdf +0 -0
- agent_env/task_step/task_steps/a2a_agent_validator/fixtures/red.gif +0 -0
- agent_env/task_step/task_steps/a2a_agent_validator/fixtures/red.jpg +0 -0
- agent_env/task_step/task_steps/a2a_agent_validator/fixtures/red.png +0 -0
- agent_env/task_step/task_steps/a2a_agent_validator/verify_a2a_agent_card.py +103 -0
- agent_env/task_step/task_steps/a2a_agent_validator/verify_a2a_agent_config_identity.py +69 -0
- agent_env/task_step/task_steps/a2a_agent_validator/verify_a2a_agent_mcp.py +178 -0
- agent_env/task_step/task_steps/a2a_agent_validator/verify_a2a_core_protocol.py +67 -0
- agent_env/task_step/task_steps/a2a_agent_validator/verify_a2a_install.py +115 -0
- agent_env/task_step/task_steps/a2a_agent_validator/verify_a2a_litellm_attribution.py +124 -0
- agent_env/task_step/task_steps/a2a_agent_validator/verify_a2a_litellm_attribution_runtime.py +173 -0
- agent_env/task_step/task_steps/a2a_agent_validator/verify_a2a_modalities.py +270 -0
- agent_env/task_step/task_steps/a2a_agent_validator/verify_a2a_peer_agents.py +102 -0
- agent_env/task_step/task_steps/a2a_agent_validator/verify_a2a_role.py +151 -0
- agent_env/task_step/task_steps/a2a_agent_validator/verify_a2a_skill_config.py +194 -0
- agent_env/task_step/task_steps/a2a_agent_validator/verify_a2a_snapshot.py +111 -0
- agent_env/task_step/task_steps/a2a_agent_validator/verify_a2a_system_prompt.py +215 -0
- agent_env/task_step/task_steps/a2a_agent_validator/verify_a2a_trajectory.py +164 -0
- agent_env/task_step/task_steps/add_skills.py +240 -0
- agent_env/task_step/task_steps/apply_server_config.py +288 -0
- agent_env/task_step/task_steps/collect_artifacts.py +940 -0
- agent_env/task_step/task_steps/deploy_agent.py +626 -0
- agent_env/task_step/task_steps/deploy_env.py +197 -0
- agent_env/task_step/task_steps/deploy_human_agent.py +100 -0
- agent_env/task_step/task_steps/deploy_sandbox.py +170 -0
- agent_env/task_step/task_steps/env_card_validator/__init__.py +4 -0
- agent_env/task_step/task_steps/env_card_validator/verify_env_card.py +109 -0
- agent_env/task_step/task_steps/env_card_validator/verify_env_core_protocol.py +108 -0
- agent_env/task_step/task_steps/install_agent.py +376 -0
- agent_env/task_step/task_steps/load_artifact.py +775 -0
- agent_env/task_step/task_steps/mcp_cli_builder/__init__.py +4 -0
- agent_env/task_step/task_steps/mcp_cli_builder/build_mcp_cli.py +201 -0
- agent_env/task_step/task_steps/mcp_cli_builder/codegen.py +627 -0
- agent_env/task_step/task_steps/mcp_env_validator/__init__.py +44 -0
- agent_env/task_step/task_steps/mcp_env_validator/validation_gate_aggregator.py +102 -0
- agent_env/task_step/task_steps/mcp_env_validator/verify_mcp_env_assessment.py +78 -0
- agent_env/task_step/task_steps/mcp_env_validator/verify_mcp_tool_schema.py +152 -0
- agent_env/task_step/task_steps/mcp_env_validator/verify_spec_conformance.py +486 -0
- agent_env/task_step/task_steps/modify_env_tool_access.py +80 -0
- agent_env/task_step/task_steps/multienv_validator/__init__.py +3 -0
- agent_env/task_step/task_steps/multienv_validator/combine_universe_verdicts.py +107 -0
- agent_env/task_step/task_steps/multienv_validator/universe_comparison.py +253 -0
- agent_env/task_step/task_steps/multienv_validator/verify_universe_agent_judge.py +180 -0
- agent_env/task_step/task_steps/multienv_validator/verify_universe_roundtrip.py +241 -0
- agent_env/task_step/task_steps/peer_agents.py +92 -0
- agent_env/task_step/task_steps/prompt_agent.py +900 -0
- agent_env/task_step/task_steps/register_agent_triggers.py +113 -0
- agent_env/task_step/task_steps/register_env_triggers.py +97 -0
- agent_env/task_step/task_steps/reset_env.py +71 -0
- agent_env/task_step/task_steps/review.py +152 -0
- agent_env/task_step/task_steps/run_code.py +461 -0
- agent_env/task_step/task_steps/run_code_runner.py +52 -0
- agent_env/task_step/task_steps/run_docker_container.py +380 -0
- agent_env/task_step/task_steps/sandbox_utils/__init__.py +0 -0
- agent_env/task_step/task_steps/sandbox_utils/sandbox_utils.py +79 -0
- agent_env/task_step/task_steps/snapshot_agent_state.py +205 -0
- agent_env/task_step/task_steps/snapshot_env.py +637 -0
- agent_env/task_step/task_steps/sync_env_clock.py +132 -0
- agent_env/task_step/task_steps/teardown_sandboxes.py +170 -0
- agent_env/task_step/task_steps/verifiers/__init__.py +23 -0
- agent_env/task_step/task_steps/verifiers/agent_prompt_response_verifier.py +166 -0
- agent_env/task_step/task_steps/verifiers/aggregate_verifiers.py +90 -0
- agent_env/task_step/task_steps/verifiers/env_outcome_verifier.py +127 -0
- agent_env/task_step/task_steps/verifiers/judge_utils/__init__.py +2 -0
- agent_env/task_step/task_steps/verifiers/judge_utils/frame_selection.py +377 -0
- agent_env/task_step/task_steps/verifiers/judge_utils/judge_output_format.py +1293 -0
- agent_env/task_step/task_steps/verifiers/judge_utils/trajectory_filter.py +514 -0
- agent_env/task_step/task_steps/verifiers/rubrics_verifier.py +1318 -0
- agent_env/task_step/task_steps/verifiers/run_container_unit_tests_verifier.py +434 -0
- agent_env/task_step/task_steps/verifiers/scoring.py +40 -0
- agent_env/task_step/task_steps/verifiers/verify_sandbox.py +308 -0
- agent_env/task_step/thread_work.py +61 -0
- agent_env/utils/__init__.py +0 -0
- agent_env/utils/card_naming.py +108 -0
- agent_env/utils/deprecation.py +109 -0
- agent_env/utils/docker_build.py +29 -0
- agent_env/utils/exec_retry.py +77 -0
- agent_env/utils/litellm_attribution.py +59 -0
- agent_env/utils/paths.py +20 -0
- agentenv_framework-0.9.1254.dist-info/METADATA +394 -0
- agentenv_framework-0.9.1254.dist-info/RECORD +312 -0
- agentenv_framework-0.9.1254.dist-info/WHEEL +4 -0
- agentenv_framework-0.9.1254.dist-info/entry_points.txt +5 -0
- agentenv_framework-0.9.1254.dist-info/licenses/LICENSE +202 -0
- agentenv_framework-0.9.1254.dist-info/licenses/NOTICE +4 -0
- agentenv_framework-0.9.1254.dist-info/licenses/THIRD_PARTY_NOTICES.md +7665 -0
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"""Build a CLI from a deployed server's manifest and live MCP tools."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import functools
|
|
6
|
+
import logging
|
|
7
|
+
import os
|
|
8
|
+
import tempfile
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, ClassVar, Optional
|
|
11
|
+
|
|
12
|
+
import httpx
|
|
13
|
+
|
|
14
|
+
from agentenv_protocol import GET_INTERFACES_EXTENSION_URI, INTERFACE_MANIFEST_PATH
|
|
15
|
+
from agentenv_protocol import client as protocol_v1
|
|
16
|
+
|
|
17
|
+
from agent_env.artifact import CliArtifact
|
|
18
|
+
from agent_env.artifact.store import artifact_write_lock
|
|
19
|
+
from agent_env.env import Env
|
|
20
|
+
from agent_env.env.env import require_gateway_url
|
|
21
|
+
from agent_env.task_step.context import TaskStepContext
|
|
22
|
+
from agent_env.entity_refs import EntityRef, RefRole
|
|
23
|
+
from agent_env.task_step.task_step import TaskStep, TaskStepDependency
|
|
24
|
+
from agent_env.task_step.thread_work import finish_on_thread
|
|
25
|
+
from agent_env.task_step.task_steps.mcp_cli_builder.codegen import generate_cli_script
|
|
26
|
+
|
|
27
|
+
logger = logging.getLogger(__name__)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
async def _resolve_manifest_endpoint(gateway_url: str, environment_name: str) -> str | None:
|
|
31
|
+
"""The server's advertised manifest index path, gateway-rewritten and reachable as-is.
|
|
32
|
+
|
|
33
|
+
Returns None on every failure rather than raising: until the fleet is rebuilt,
|
|
34
|
+
having nothing to advertise is the normal case, so these are debug-level.
|
|
35
|
+
"""
|
|
36
|
+
try:
|
|
37
|
+
card = await protocol_v1.get_card(gateway_url)
|
|
38
|
+
except (httpx.HTTPError, ValueError) as e:
|
|
39
|
+
logger.debug("env card for %r unavailable (%s); constructing the manifest path", environment_name, e)
|
|
40
|
+
return None
|
|
41
|
+
|
|
42
|
+
child = protocol_v1.find_child(card, environment_name)
|
|
43
|
+
if child is None:
|
|
44
|
+
logger.debug("env card has no child named %r; constructing the manifest path", environment_name)
|
|
45
|
+
return None
|
|
46
|
+
return protocol_v1.extension_params(child, GET_INTERFACES_EXTENSION_URI).get("endpoint")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
async def fetch_interface_manifest(
|
|
50
|
+
gateway_url: str,
|
|
51
|
+
environment_name: str,
|
|
52
|
+
) -> dict[str, Any] | None:
|
|
53
|
+
"""Fetch a deployed server's CLI manifest, or None when none is usable.
|
|
54
|
+
|
|
55
|
+
The index path comes off the card when the server advertises it, and is
|
|
56
|
+
constructed the old way when it does not.
|
|
57
|
+
|
|
58
|
+
Contract: *degrade the unreachable, guard the misconfigured*. A missing,
|
|
59
|
+
unreachable, or non-manifest response (404, other non-2xx incl. a transient
|
|
60
|
+
502, transport error, non-JSON, or JSON without a ``service`` field) returns
|
|
61
|
+
None so the caller still builds the legacy flat CLI — building a CLI must not
|
|
62
|
+
depend on a manifest being served. Only a server that actually served a
|
|
63
|
+
manifest object for the wrong ``service`` raises: that is an opted-in server
|
|
64
|
+
that is misconfigured, a real bug worth failing on.
|
|
65
|
+
"""
|
|
66
|
+
endpoint = await _resolve_manifest_endpoint(gateway_url, environment_name)
|
|
67
|
+
if not endpoint:
|
|
68
|
+
# Fallback until every image advertises the extension; the server owns
|
|
69
|
+
# this path and the gateway owns the /svc/mcp-{name} prefix, not the renderer.
|
|
70
|
+
endpoint = f"/svc/mcp-{environment_name}{INTERFACE_MANIFEST_PATH}"
|
|
71
|
+
try:
|
|
72
|
+
manifest = await protocol_v1.get_interface_manifest(
|
|
73
|
+
gateway_url, "cli", endpoint=endpoint
|
|
74
|
+
)
|
|
75
|
+
except httpx.HTTPStatusError as e:
|
|
76
|
+
# 404 = opted out; any other error status (405, 5xx, a transient 502
|
|
77
|
+
# while the server is still booting) is treated the same: no usable
|
|
78
|
+
# manifest, fall back to the flat CLI rather than fail the build.
|
|
79
|
+
if e.response.status_code != 404:
|
|
80
|
+
logger.warning(
|
|
81
|
+
"Interface Manifest endpoint for %r returned HTTP %s; building flat CLI",
|
|
82
|
+
environment_name, e.response.status_code,
|
|
83
|
+
)
|
|
84
|
+
return None
|
|
85
|
+
except httpx.HTTPError as e:
|
|
86
|
+
logger.warning(
|
|
87
|
+
"Interface Manifest fetch for %r failed (%s); building flat CLI",
|
|
88
|
+
environment_name, e,
|
|
89
|
+
)
|
|
90
|
+
return None
|
|
91
|
+
except ValueError: # includes json.JSONDecodeError
|
|
92
|
+
logger.warning(
|
|
93
|
+
"Interface Manifest for %r was not JSON; building flat CLI", environment_name
|
|
94
|
+
)
|
|
95
|
+
return None
|
|
96
|
+
if not isinstance(manifest, dict):
|
|
97
|
+
logger.warning(
|
|
98
|
+
"Interface Manifest for %r was not a JSON object; building flat CLI",
|
|
99
|
+
environment_name,
|
|
100
|
+
)
|
|
101
|
+
return None
|
|
102
|
+
|
|
103
|
+
served_service = manifest.get("service")
|
|
104
|
+
if served_service is None:
|
|
105
|
+
# Not actually a manifest (e.g. an unrelated 200 body); degrade quietly.
|
|
106
|
+
logger.warning(
|
|
107
|
+
"Interface Manifest endpoint for %r returned JSON without a 'service' "
|
|
108
|
+
"field; building flat CLI", environment_name,
|
|
109
|
+
)
|
|
110
|
+
return None
|
|
111
|
+
if served_service != environment_name:
|
|
112
|
+
raise ValueError(
|
|
113
|
+
f"Interface Manifest service {served_service!r} does not "
|
|
114
|
+
f"match deployed service {environment_name!r}"
|
|
115
|
+
)
|
|
116
|
+
# manifest_version is checked in codegen, alongside the interface and shape.
|
|
117
|
+
return manifest
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class BuildMcpCliTaskStep(TaskStep):
|
|
121
|
+
type: ClassVar[str] = "build_mcp_cli"
|
|
122
|
+
entity_refs = (
|
|
123
|
+
EntityRef.env("env_id"),
|
|
124
|
+
EntityRef.artifact("cli_artifact_id", role=RefRole.OUTPUT, artifact_type="cli"),
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
def __init__(
|
|
128
|
+
self,
|
|
129
|
+
id: str,
|
|
130
|
+
version: Optional[int],
|
|
131
|
+
env_id: str,
|
|
132
|
+
command_name: str,
|
|
133
|
+
cli_artifact_id: Optional[str] = None,
|
|
134
|
+
depends_on: Optional[list[TaskStepDependency]] = None,
|
|
135
|
+
fail_task_on_error: bool = True,
|
|
136
|
+
):
|
|
137
|
+
super().__init__(id, version, depends_on=depends_on, fail_task_on_error=fail_task_on_error)
|
|
138
|
+
self.env_id = env_id
|
|
139
|
+
self.command_name = command_name
|
|
140
|
+
self.cli_artifact_id = cli_artifact_id or f"cli-{env_id}"
|
|
141
|
+
|
|
142
|
+
def to_dict(self) -> dict:
|
|
143
|
+
base = super().to_dict()
|
|
144
|
+
base["env_id"] = self.env_id
|
|
145
|
+
base["command_name"] = self.command_name
|
|
146
|
+
base["cli_artifact_id"] = self.cli_artifact_id
|
|
147
|
+
return base
|
|
148
|
+
|
|
149
|
+
@classmethod
|
|
150
|
+
def from_dict(cls, data: dict) -> BuildMcpCliTaskStep:
|
|
151
|
+
return cls(
|
|
152
|
+
**cls._base_from_dict(data),
|
|
153
|
+
env_id=data["env_id"],
|
|
154
|
+
command_name=data["command_name"],
|
|
155
|
+
cli_artifact_id=data.get("cli_artifact_id"),
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
async def execute(self, context: TaskStepContext) -> TaskStepContext:
|
|
159
|
+
deployed = next((d for d in context.deployed_envs if d.env_id == self.env_id), None)
|
|
160
|
+
if deployed is None:
|
|
161
|
+
raise RuntimeError(f"Env '{self.env_id}' not found in context.deployed_envs")
|
|
162
|
+
gateway_url = require_gateway_url(deployed, "build_mcp_cli") # the CLI it generates talks to the gateway
|
|
163
|
+
|
|
164
|
+
env = Env.get(self.env_id, deployed.env_version)
|
|
165
|
+
environment_name = getattr(env, "environment_name", None)
|
|
166
|
+
if not environment_name:
|
|
167
|
+
raise RuntimeError(f"Env '{self.env_id}' has no environment_name")
|
|
168
|
+
manifest = await fetch_interface_manifest(gateway_url, environment_name)
|
|
169
|
+
script_source = generate_cli_script(
|
|
170
|
+
self.command_name,
|
|
171
|
+
interface_manifest=manifest,
|
|
172
|
+
environment_name=environment_name,
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
artifact = await finish_on_thread(
|
|
176
|
+
functools.partial(self._put_cli, script_source, deployed.env_version),
|
|
177
|
+
f"Uploading CLI {self.cli_artifact_id}",
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
context.metadata["cli_artifact"] = {"id": artifact.id, "version": artifact.version, "type": artifact.type}
|
|
181
|
+
logger.info(f"Created CliArtifact: id={artifact.id} version={artifact.version} command_name={artifact.command_name}")
|
|
182
|
+
return context
|
|
183
|
+
|
|
184
|
+
def _put_cli(self, script_source: str, env_version: Optional[int]) -> CliArtifact:
|
|
185
|
+
"""Stage the CLI bundle in a temp dir and upload it as the CliArtifact, one write per id at
|
|
186
|
+
a time in this process."""
|
|
187
|
+
with artifact_write_lock(self.cli_artifact_id), tempfile.TemporaryDirectory() as tmp_str:
|
|
188
|
+
cli_dir = Path(tmp_str)
|
|
189
|
+
bin_dir = cli_dir / "bin"
|
|
190
|
+
bin_dir.mkdir()
|
|
191
|
+
entrypoint_path = bin_dir / self.command_name
|
|
192
|
+
entrypoint_path.write_text(script_source)
|
|
193
|
+
os.chmod(entrypoint_path, 0o755)
|
|
194
|
+
return CliArtifact.put(
|
|
195
|
+
id=self.cli_artifact_id,
|
|
196
|
+
command_name=self.command_name,
|
|
197
|
+
entrypoint=f"bin/{self.command_name}",
|
|
198
|
+
cli_dir=cli_dir,
|
|
199
|
+
env_id=self.env_id,
|
|
200
|
+
env_version=env_version,
|
|
201
|
+
)
|