agentseek-api 0.2.3__tar.gz → 0.3.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.
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/.github/workflows/ci.yml +33 -6
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/CHANGELOG.md +39 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/PKG-INFO +46 -2
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/README.md +44 -1
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/README.zh-CN.md +38 -1
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/pyproject.toml +6 -1
- agentseek_api-0.3.0/scripts/container_image_archive.py +296 -0
- agentseek_api-0.3.0/scripts/test-cli-docker.sh +376 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/scripts/test-redis-runtime.sh +48 -3
- agentseek_api-0.3.0/scripts/test_cli_config_autodiscovery.py +123 -0
- agentseek_api-0.3.0/scripts/test_container_env_boundary.py +920 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/scripts/test_minimum_cli_dependencies.py +15 -0
- agentseek_api-0.3.0/scripts/value_free_log_tail.py +46 -0
- agentseek_api-0.3.0/src/agentseek_api/__init__.py +1 -0
- agentseek_api-0.3.0/src/agentseek_api/cli.py +1722 -0
- agentseek_api-0.3.0/src/agentseek_api/container_build.py +2831 -0
- agentseek_api-0.3.0/src/agentseek_api/container_policy.py +350 -0
- agentseek_api-0.3.0/src/agentseek_api/docker_runtime.py +720 -0
- agentseek_api-0.3.0/src/agentseek_api/dotenv_adapter.py +141 -0
- agentseek_api-0.3.0/src/agentseek_api/environment.py +509 -0
- agentseek_api-0.3.0/src/agentseek_api/runtime_entrypoint.py +201 -0
- agentseek_api-0.3.0/src/agentseek_api/secure_temp.py +1046 -0
- agentseek_api-0.3.0/tests/container_plan_helpers.py +315 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_cli_runtime_processes.py +173 -0
- agentseek_api-0.3.0/tests/unit/test_ci_workflow.py +111 -0
- agentseek_api-0.3.0/tests/unit/test_cli.py +4414 -0
- agentseek_api-0.3.0/tests/unit/test_container_boundary_acceptance.py +606 -0
- agentseek_api-0.3.0/tests/unit/test_container_build.py +2332 -0
- agentseek_api-0.3.0/tests/unit/test_container_image_archive.py +364 -0
- agentseek_api-0.3.0/tests/unit/test_container_policy.py +591 -0
- agentseek_api-0.3.0/tests/unit/test_docker_runtime.py +1372 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_dotenv_adapter.py +31 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_redis_runtime_script.py +31 -1
- agentseek_api-0.3.0/tests/unit/test_release_artifacts.py +14 -0
- agentseek_api-0.3.0/tests/unit/test_secure_temp.py +705 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/uv.lock +3 -1
- agentseek_api-0.2.3/scripts/test-cli-docker.sh +0 -172
- agentseek_api-0.2.3/scripts/test_cli_config_autodiscovery.py +0 -55
- agentseek_api-0.2.3/src/agentseek_api/__init__.py +0 -1
- agentseek_api-0.2.3/src/agentseek_api/cli.py +0 -1117
- agentseek_api-0.2.3/src/agentseek_api/dotenv_adapter.py +0 -69
- agentseek_api-0.2.3/src/agentseek_api/runtime_entrypoint.py +0 -59
- agentseek_api-0.2.3/tests/unit/test_ci_workflow.py +0 -25
- agentseek_api-0.2.3/tests/unit/test_cli.py +0 -2193
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/.dockerignore +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/.env.example +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/.github/workflows/live-provider-streaming.yml +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/.github/workflows/release.yml +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/.gitignore +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/AGENTS.md +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/CONTRIBUTING.md +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/LICENSE +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/Makefile +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/README.md +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/__init__.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/assistant_config/agentseek.json +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/assistant_config/graph.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/auth/api_key_auth.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/auth/bearer_token_auth.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/auth/custom_backend.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/auth/jwt.md +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/auth/jwt_auth.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/custom_routes/app.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/docker_ci_auth/auth_backend.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/docker_ci_auth/manifest.json +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/external_graph/__init__.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/external_graph/graph.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/external_graph/manifest.json +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/external_graph/run.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/graphs/__init__.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/graphs/react_agent/__init__.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/graphs/react_agent/graph.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/graphs/store_memory/graph.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/graphs/stress_test/__init__.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/graphs/stress_test/graph.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/graphs/stress_tool_agent/__init__.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/graphs/stress_tool_agent/graph.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/graphs/subgraph_agent/__init__.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/graphs/subgraph_agent/graph.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/graphs/subgraph_hitl_agent/__init__.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/graphs/subgraph_hitl_agent/graph.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/live_provider_graphs/__init__.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/live_provider_graphs/graph.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/live_provider_graphs/manifest.json +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/minimal_agentseek/agentseek.json +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/minimal_agentseek/graph.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/run_sample_graphs.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/examples/sample_graphs_manifest.json +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/scripts/build_live_provider_matrix.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/scripts/seekdb_checkpoint_smoke.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/scripts/seekdb_embed_launcher.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/scripts/test-checkpoints.sh +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/scripts/test-cli-dev-samples.sh +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/scripts/test-live-provider.sh +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/scripts/test-seekdb.sh +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/scripts/test_cli_dev_studio_smoke.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/scripts/test_cli_embed_serve_smoke.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/scripts/test_cli_serve_smoke.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/scripts/verify_docker_api.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/scripts/verify_worker_concurrency.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/a2a_server.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/api/__init__.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/api/assistants.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/api/crons.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/api/runs.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/api/stateless_runs.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/api/store.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/api/streaming.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/api/threads.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/constants.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/core/__init__.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/core/a2a_config.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/core/app_loader.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/core/auth_deps.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/core/auth_middleware.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/core/config_file.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/core/content_type_fix.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/core/cors_config.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/core/database.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/core/http_config.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/core/mcp_config.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/core/oceanbase_checkpointer.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/core/orm.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/core/runtime_store.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/core/sqlite_checkpointer.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/core/store_config.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/main.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/mcp_server.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/models/__init__.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/models/api.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/models/auth.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/models/protocol.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/process_supervisor.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/scheduler.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/services/__init__.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/services/cron_models.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/services/cron_rrule.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/services/cron_scheduler.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/services/cron_service.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/services/cron_webhooks.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/services/default_assistants.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/services/executor.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/services/langgraph_service.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/services/redis_queue.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/services/run_executor.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/services/run_jobs.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/services/run_preparation.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/services/run_state.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/services/sample_graphs.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/services/sse.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/services/stream_modes.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/services/stream_persistence.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/services/thread_checkpoint_store.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/services/thread_protocol.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/services/thread_service.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/settings.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/src/agentseek_api/worker.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/__init__.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/conftest.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/e2e/__init__.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/e2e/conftest.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/e2e/e2e_inprocess_flow.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/e2e/e2e_live_http_flow.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/e2e/e2e_live_http_multi_graph.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/e2e/e2e_live_http_resume_flow.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/e2e/fixtures/langgraph.store-e2e.json +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/e2e/live_provider_helpers.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/e2e/test_a2a_live.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/e2e/test_cron_api_live.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/e2e/test_cron_scheduler_live.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/e2e/test_langsmith_compat_live.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/e2e/test_live_provider_api.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/e2e/test_live_provider_mcp.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/e2e/test_mcp_live.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/e2e/test_real_api_flow.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/fixtures/runtime_settings_probe/sitecustomize.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/fixtures/termination_tree.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_a2a_endpoint.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_agent_card.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_agents_aliases.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_assistants_compat.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_assistants_config_context_sync.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_assistants_crud.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_assistants_ordering.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_auth_coverage_gaps.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_auth_handlers.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_auth_route_enforcement.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_auth_scope_matrix.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_core_api_flow.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_cron_api.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_langsmith_compat_coverage.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_langsmith_compat_extra.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_live_provider_streaming.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_live_redis_queue.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_live_redis_stream_persistence.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_mcp_endpoint.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_metadata_db_config.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_protocol_v2_streaming.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_run_cancellation_async.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_runs_compat.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_runs_crud.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_runs_ordering.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_runs_resume.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_runs_streaming.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_runs_streaming_errors.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_runs_validation.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_runs_wait_and_get.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_runs_wait_timeout.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_scheduler_runtime.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_stateless_runs_crud.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_store_api.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_stream_persistence.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_system_endpoints.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_threads_compat.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_threads_crud.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/integration/test_threads_ordering.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_a2a_config.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_a2a_server.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_app_loader.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_assistant_routes_unit.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_auth.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_auth_deps.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_auth_internals.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_checkpoint_guards.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_content_type_fix.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_cron_rrule.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_cron_service.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_cron_webhooks.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_custom_routers.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_database_manager.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_default_assistants.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_examples.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_executor.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_graph_manifest.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_http_config.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_langgraph_service.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_live_provider_e2e_config.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_live_provider_graphs.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_live_provider_matrix.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_mcp_config.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_mcp_server.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_oceanbase_checkpointer.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_openapi_auth_config.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_process_supervisor.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_redis_queue.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_run_controls_validation.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_run_executor.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_run_jobs.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_run_preparation.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_run_state.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_runtime_entrypoint.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_runtime_environment.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_runtime_store.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_sample_graphs.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_scheduler.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_scheduler_process.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_seekdb_checkpoint_smoke.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_seekdb_embed_launcher.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_sqlite_checkpointer.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_sse_keepalive.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_store_config.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_stream_modes.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_stream_persistence_helpers.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_thread_checkpoint_store.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_thread_protocol.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_thread_routes_unit.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_thread_service.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_worker.py +0 -0
- {agentseek_api-0.2.3 → agentseek_api-0.3.0}/tests/unit/test_worker_concurrency_probe.py +0 -0
|
@@ -44,7 +44,13 @@ jobs:
|
|
|
44
44
|
run: uv sync --dev
|
|
45
45
|
|
|
46
46
|
- name: Coverage-backed unit + integration suite
|
|
47
|
-
run:
|
|
47
|
+
run: >-
|
|
48
|
+
uv run pytest tests/unit tests/integration
|
|
49
|
+
-m 'not docker'
|
|
50
|
+
--cov=src/agentseek_api
|
|
51
|
+
--cov-report=term-missing
|
|
52
|
+
--cov-fail-under=90
|
|
53
|
+
-q
|
|
48
54
|
|
|
49
55
|
cli-compatibility:
|
|
50
56
|
name: CLI Compatibility (${{ matrix.os }})
|
|
@@ -78,9 +84,7 @@ jobs:
|
|
|
78
84
|
run: uv run python scripts/test_cli_config_autodiscovery.py
|
|
79
85
|
|
|
80
86
|
- name: Dockerfile command renders a runnable config
|
|
81
|
-
run:
|
|
82
|
-
uv run python -c
|
|
83
|
-
"from pathlib import Path; import subprocess; out = Path('.tmp/agentseek.Dockerfile'); out.parent.mkdir(exist_ok=True); subprocess.run(['uv', 'run', 'agentseek-api', 'dockerfile', '--config', 'examples/external_graph/manifest.json', str(out)], check=True); text = out.read_text(encoding='utf-8'); assert 'ENV PYTHONPATH=/deps/agent' in text; assert 'ENV AGENTSEEK_GRAPHS=/deps/agent/examples/external_graph/manifest.json' in text"
|
|
87
|
+
run: uv run python scripts/test_cli_config_autodiscovery.py --config examples/external_graph/manifest.json
|
|
84
88
|
|
|
85
89
|
- name: CLI config, host environment, and process tests
|
|
86
90
|
run: >-
|
|
@@ -92,6 +96,8 @@ jobs:
|
|
|
92
96
|
tests/unit/test_runtime_entrypoint.py
|
|
93
97
|
tests/unit/test_process_supervisor.py
|
|
94
98
|
tests/unit/test_sqlite_checkpointer.py
|
|
99
|
+
tests/unit/test_container_policy.py
|
|
100
|
+
tests/unit/test_secure_temp.py
|
|
95
101
|
tests/integration/test_cli_runtime_processes.py
|
|
96
102
|
tests/integration/test_metadata_db_config.py
|
|
97
103
|
-q
|
|
@@ -130,9 +136,15 @@ jobs:
|
|
|
130
136
|
run: uv run python scripts/test_cli_embed_serve_smoke.py
|
|
131
137
|
|
|
132
138
|
cli-docker-runtime:
|
|
133
|
-
name: CLI Docker Runtime
|
|
139
|
+
name: CLI Docker Runtime (Compose ${{ matrix.compose-version }})
|
|
134
140
|
runs-on: ubuntu-latest
|
|
135
|
-
timeout-minutes:
|
|
141
|
+
timeout-minutes: 45
|
|
142
|
+
strategy:
|
|
143
|
+
fail-fast: false
|
|
144
|
+
matrix:
|
|
145
|
+
compose-version:
|
|
146
|
+
- runner-current
|
|
147
|
+
- v2.24.0
|
|
136
148
|
steps:
|
|
137
149
|
- name: Checkout
|
|
138
150
|
uses: actions/checkout@v4
|
|
@@ -147,6 +159,21 @@ jobs:
|
|
|
147
159
|
- name: Sync dependencies
|
|
148
160
|
run: uv sync --dev
|
|
149
161
|
|
|
162
|
+
- name: Install Compose floor
|
|
163
|
+
if: matrix.compose-version == 'v2.24.0'
|
|
164
|
+
uses: docker/setup-compose-action@2fe291b7677a45ee1269ec56a42604c143505e7e # v1
|
|
165
|
+
with:
|
|
166
|
+
version: v2.24.0
|
|
167
|
+
|
|
168
|
+
- name: Record Compose version
|
|
169
|
+
run: docker compose version --short
|
|
170
|
+
|
|
171
|
+
- name: Container environment boundary
|
|
172
|
+
run: uv run python scripts/test_container_env_boundary.py
|
|
173
|
+
|
|
174
|
+
- name: Docker and Compose regression suite
|
|
175
|
+
run: uv run pytest tests/unit/test_docker_runtime.py -m docker -q
|
|
176
|
+
|
|
150
177
|
- name: CLI Docker smoke
|
|
151
178
|
run: make test-cli-docker
|
|
152
179
|
|
|
@@ -4,6 +4,45 @@ Notable changes to AgentSeek API are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.3.0 - 2026-08-20
|
|
8
|
+
|
|
9
|
+
### Highlights
|
|
10
|
+
|
|
11
|
+
- Added the explicit `preloaded-v1` container environment contract so the host
|
|
12
|
+
remains the single owner of application environment resolution.
|
|
13
|
+
- Generated containers install and attest the exact
|
|
14
|
+
`agentseek-api[embedded]==0.3.0` runtime independently of project
|
|
15
|
+
dependencies.
|
|
16
|
+
- Hardened Docker, Compose, and build-context boundaries so application values
|
|
17
|
+
are carried only to the declared runtime target and credential-bearing source
|
|
18
|
+
files are excluded from image inputs.
|
|
19
|
+
- Defined compatible custom-image labels and runtime-manifest checks for
|
|
20
|
+
`preloaded-v1` launches.
|
|
21
|
+
|
|
22
|
+
### Upgrade notes
|
|
23
|
+
|
|
24
|
+
- Container projects that pin `agentseek-api==0.2.2`, or otherwise exclude
|
|
25
|
+
`0.3.0`, must update their dependency constraint before using generated
|
|
26
|
+
images.
|
|
27
|
+
- Custom images must publish the `preloaded-v1` environment-contract, runtime
|
|
28
|
+
manifest, distribution, and version labels; older images must keep using the
|
|
29
|
+
older launcher until migrated.
|
|
30
|
+
- `dockerfile` now writes a complete build-bundle directory. Declare only
|
|
31
|
+
reviewed project paths with `build_include`; credentialed `pip_config_file`
|
|
32
|
+
input is delivered as a BuildKit secret rather than copied into the bundle.
|
|
33
|
+
- Container application values cross only an explicit runtime boundary:
|
|
34
|
+
`--pass-env` selects the direct Docker carrier, while `compose_env` and
|
|
35
|
+
`--compose-pass-env` select the private Compose dotenv carrier. All are
|
|
36
|
+
trusted-input declarations and none authorize values to enter the build.
|
|
37
|
+
- Custom images must provide the exact `org.agentseek.environment-contract`,
|
|
38
|
+
`org.agentseek.runtime-manifest`, `org.agentseek.runtime-distribution`, and
|
|
39
|
+
`org.agentseek.runtime-version` labels and matching manifest/runtime state.
|
|
40
|
+
There is no legacy-image fallback.
|
|
41
|
+
- Release Train A shipped API 0.2.3, templates 0.1.3, and AgentSeek 0.1.3. The
|
|
42
|
+
planned Train B template/catalog release is 0.1.4. The separate planned
|
|
43
|
+
AgentSeek release is also 0.1.4; both follow their shipped 0.1.3 releases and
|
|
44
|
+
remain separate release gates.
|
|
45
|
+
|
|
7
46
|
## 0.2.3 - 2026-08-17
|
|
8
47
|
|
|
9
48
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: agentseek-api
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: AgentSeek API core runtime with OceanBase checkpoints.
|
|
5
5
|
License-File: LICENSE
|
|
6
6
|
Requires-Python: >=3.12
|
|
@@ -18,6 +18,7 @@ Requires-Dist: langchain>=0.3.9
|
|
|
18
18
|
Requires-Dist: langgraph-sdk>=0.3.5
|
|
19
19
|
Requires-Dist: langgraph>=1.0.6
|
|
20
20
|
Requires-Dist: mcp<2,>=1.27.1
|
|
21
|
+
Requires-Dist: packaging>=24.0
|
|
21
22
|
Requires-Dist: pydantic-settings>=2.4.0
|
|
22
23
|
Requires-Dist: pydantic>=2.8.0
|
|
23
24
|
Requires-Dist: pymysql>=1.1.0
|
|
@@ -328,7 +329,7 @@ keys with similar names.
|
|
|
328
329
|
uv run agentseek-api dev
|
|
329
330
|
uv run agentseek-api serve --config ./langgraph.json --port 8080
|
|
330
331
|
uv run agentseek-api worker --config ./langgraph.json
|
|
331
|
-
uv run agentseek-api dockerfile --config ./langgraph.json ./
|
|
332
|
+
uv run agentseek-api dockerfile --config ./langgraph.json ./agentseek-build-bundle
|
|
332
333
|
uv run agentseek-api build --config ./langgraph.json -t agentseek-api:dev
|
|
333
334
|
uv run agentseek-api up --config ./langgraph.json --port 8123 --wait
|
|
334
335
|
uv run agentseek-api version
|
|
@@ -355,9 +356,18 @@ uv run agentseek-api version
|
|
|
355
356
|
- `build`
|
|
356
357
|
- Use `-t, --tag` to set the image tag
|
|
357
358
|
- Supports `--platform`, `--pull`, and `--no-pull`
|
|
359
|
+
- `dockerfile`
|
|
360
|
+
- Writes a complete private build-bundle directory, including the generated
|
|
361
|
+
`Dockerfile`, sanitized runtime manifest, and selected project files; the
|
|
362
|
+
output argument is a new directory, not a standalone Dockerfile path
|
|
358
363
|
- `up`
|
|
359
364
|
- Supports `--wait`, `--image`, `--base-image`, `--postgres-uri`,
|
|
360
365
|
`--recreate`, and `--no-recreate`
|
|
366
|
+
- `--pass-env NAME` explicitly selects a resolved application value for the
|
|
367
|
+
direct Docker carrier; the value is inherited by name and never put in argv
|
|
368
|
+
- `--compose-pass-env NAME` explicitly selects a resolved application value
|
|
369
|
+
for Compose interpolation; config can make the same selection with
|
|
370
|
+
`compose_env`
|
|
361
371
|
|
|
362
372
|
Some LangGraph CLI-shaped flags are parsed for command compatibility but
|
|
363
373
|
rejected when their runtime behavior is not implemented yet. For mocked,
|
|
@@ -436,6 +446,40 @@ Useful config fields:
|
|
|
436
446
|
- `http.disable_a2a`: disable the A2A endpoint and agent-card discovery route
|
|
437
447
|
- `base_image`, `python_version`, `image_distro`, `pip_config_file`,
|
|
438
448
|
`dockerfile_lines`: Docker build customization fields
|
|
449
|
+
- `build_include`: additional trusted regular files or directory trees to copy
|
|
450
|
+
into the sanitized build bundle
|
|
451
|
+
- `compose_env`: names from the already-resolved application environment that
|
|
452
|
+
may cross into an explicitly selected Compose dotenv carrier
|
|
453
|
+
|
|
454
|
+
### Container migration for 0.3.0
|
|
455
|
+
|
|
456
|
+
The `preloaded-v1` contract is a breaking, fail-closed container boundary. The
|
|
457
|
+
host resolves application configuration once. Containers started from generated images receive only
|
|
458
|
+
the selected runtime payload, while ambient host values, dotenv files, package
|
|
459
|
+
credentials, and unselected Compose values stay outside the build context and
|
|
460
|
+
image layers. Use `--pass-env` or `--compose-pass-env` only for trusted input;
|
|
461
|
+
these flags authorize a value to cross the named runtime boundary, not to enter
|
|
462
|
+
the build.
|
|
463
|
+
|
|
464
|
+
`build_include` is also a trusted-input declaration: review every selected path.
|
|
465
|
+
Credentialed Python indexes belong in `pip_config_file`, which is mounted as a
|
|
466
|
+
BuildKit pip secret and is not copied into the context. The `dockerfile` command
|
|
467
|
+
now writes the complete bundle directory consumed by Docker rather than a lone
|
|
468
|
+
Dockerfile.
|
|
469
|
+
|
|
470
|
+
Custom images must expose all four exact labels:
|
|
471
|
+
|
|
472
|
+
- `org.agentseek.environment-contract=preloaded-v1`
|
|
473
|
+
- `org.agentseek.runtime-manifest=/opt/agentseek/manifest.v1.json`
|
|
474
|
+
- `org.agentseek.runtime-distribution=agentseek-api`
|
|
475
|
+
- `org.agentseek.runtime-version=0.3.0`
|
|
476
|
+
|
|
477
|
+
The manifest, installed distribution, entrypoint, and labels must agree. There
|
|
478
|
+
is no legacy-image fallback: migrate and attest the image before passing it to
|
|
479
|
+
`up --image`, or keep using the older launcher with the older image. Release
|
|
480
|
+
Train A coordinates are API 0.2.3, templates 0.1.3, and AgentSeek 0.1.3; the
|
|
481
|
+
The planned Train B template/catalog release is 0.1.4. The separate planned
|
|
482
|
+
AgentSeek release is also 0.1.4; both follow the shipped 0.1.3 releases.
|
|
439
483
|
|
|
440
484
|
Endpoint-level LangGraph config keys such as `http` and `api_version` are
|
|
441
485
|
tolerated by the CLI layer where possible. Store config is used by the HTTP
|
|
@@ -296,7 +296,7 @@ keys with similar names.
|
|
|
296
296
|
uv run agentseek-api dev
|
|
297
297
|
uv run agentseek-api serve --config ./langgraph.json --port 8080
|
|
298
298
|
uv run agentseek-api worker --config ./langgraph.json
|
|
299
|
-
uv run agentseek-api dockerfile --config ./langgraph.json ./
|
|
299
|
+
uv run agentseek-api dockerfile --config ./langgraph.json ./agentseek-build-bundle
|
|
300
300
|
uv run agentseek-api build --config ./langgraph.json -t agentseek-api:dev
|
|
301
301
|
uv run agentseek-api up --config ./langgraph.json --port 8123 --wait
|
|
302
302
|
uv run agentseek-api version
|
|
@@ -323,9 +323,18 @@ uv run agentseek-api version
|
|
|
323
323
|
- `build`
|
|
324
324
|
- Use `-t, --tag` to set the image tag
|
|
325
325
|
- Supports `--platform`, `--pull`, and `--no-pull`
|
|
326
|
+
- `dockerfile`
|
|
327
|
+
- Writes a complete private build-bundle directory, including the generated
|
|
328
|
+
`Dockerfile`, sanitized runtime manifest, and selected project files; the
|
|
329
|
+
output argument is a new directory, not a standalone Dockerfile path
|
|
326
330
|
- `up`
|
|
327
331
|
- Supports `--wait`, `--image`, `--base-image`, `--postgres-uri`,
|
|
328
332
|
`--recreate`, and `--no-recreate`
|
|
333
|
+
- `--pass-env NAME` explicitly selects a resolved application value for the
|
|
334
|
+
direct Docker carrier; the value is inherited by name and never put in argv
|
|
335
|
+
- `--compose-pass-env NAME` explicitly selects a resolved application value
|
|
336
|
+
for Compose interpolation; config can make the same selection with
|
|
337
|
+
`compose_env`
|
|
329
338
|
|
|
330
339
|
Some LangGraph CLI-shaped flags are parsed for command compatibility but
|
|
331
340
|
rejected when their runtime behavior is not implemented yet. For mocked,
|
|
@@ -404,6 +413,40 @@ Useful config fields:
|
|
|
404
413
|
- `http.disable_a2a`: disable the A2A endpoint and agent-card discovery route
|
|
405
414
|
- `base_image`, `python_version`, `image_distro`, `pip_config_file`,
|
|
406
415
|
`dockerfile_lines`: Docker build customization fields
|
|
416
|
+
- `build_include`: additional trusted regular files or directory trees to copy
|
|
417
|
+
into the sanitized build bundle
|
|
418
|
+
- `compose_env`: names from the already-resolved application environment that
|
|
419
|
+
may cross into an explicitly selected Compose dotenv carrier
|
|
420
|
+
|
|
421
|
+
### Container migration for 0.3.0
|
|
422
|
+
|
|
423
|
+
The `preloaded-v1` contract is a breaking, fail-closed container boundary. The
|
|
424
|
+
host resolves application configuration once. Containers started from generated images receive only
|
|
425
|
+
the selected runtime payload, while ambient host values, dotenv files, package
|
|
426
|
+
credentials, and unselected Compose values stay outside the build context and
|
|
427
|
+
image layers. Use `--pass-env` or `--compose-pass-env` only for trusted input;
|
|
428
|
+
these flags authorize a value to cross the named runtime boundary, not to enter
|
|
429
|
+
the build.
|
|
430
|
+
|
|
431
|
+
`build_include` is also a trusted-input declaration: review every selected path.
|
|
432
|
+
Credentialed Python indexes belong in `pip_config_file`, which is mounted as a
|
|
433
|
+
BuildKit pip secret and is not copied into the context. The `dockerfile` command
|
|
434
|
+
now writes the complete bundle directory consumed by Docker rather than a lone
|
|
435
|
+
Dockerfile.
|
|
436
|
+
|
|
437
|
+
Custom images must expose all four exact labels:
|
|
438
|
+
|
|
439
|
+
- `org.agentseek.environment-contract=preloaded-v1`
|
|
440
|
+
- `org.agentseek.runtime-manifest=/opt/agentseek/manifest.v1.json`
|
|
441
|
+
- `org.agentseek.runtime-distribution=agentseek-api`
|
|
442
|
+
- `org.agentseek.runtime-version=0.3.0`
|
|
443
|
+
|
|
444
|
+
The manifest, installed distribution, entrypoint, and labels must agree. There
|
|
445
|
+
is no legacy-image fallback: migrate and attest the image before passing it to
|
|
446
|
+
`up --image`, or keep using the older launcher with the older image. Release
|
|
447
|
+
Train A coordinates are API 0.2.3, templates 0.1.3, and AgentSeek 0.1.3; the
|
|
448
|
+
The planned Train B template/catalog release is 0.1.4. The separate planned
|
|
449
|
+
AgentSeek release is also 0.1.4; both follow the shipped 0.1.3 releases.
|
|
407
450
|
|
|
408
451
|
Endpoint-level LangGraph config keys such as `http` and `api_version` are
|
|
409
452
|
tolerated by the CLI layer where possible. Store config is used by the HTTP
|
|
@@ -262,7 +262,7 @@ agentseek-api <command> [arguments]
|
|
|
262
262
|
uv run agentseek-api dev
|
|
263
263
|
uv run agentseek-api serve --config ./langgraph.json --port 8080
|
|
264
264
|
uv run agentseek-api worker --config ./langgraph.json
|
|
265
|
-
uv run agentseek-api dockerfile --config ./langgraph.json ./
|
|
265
|
+
uv run agentseek-api dockerfile --config ./langgraph.json ./agentseek-build-bundle
|
|
266
266
|
uv run agentseek-api build --config ./langgraph.json -t agentseek-api:dev
|
|
267
267
|
uv run agentseek-api up --config ./langgraph.json --port 8123 --wait
|
|
268
268
|
uv run agentseek-api version
|
|
@@ -289,9 +289,17 @@ uv run agentseek-api version
|
|
|
289
289
|
- `build`
|
|
290
290
|
- 使用 `-t, --tag` 设置镜像 tag
|
|
291
291
|
- 支持 `--platform`、`--pull`、`--no-pull`
|
|
292
|
+
- `dockerfile`
|
|
293
|
+
- 输出完整的私有构建 bundle 目录,其中包含生成的 `Dockerfile`、净化后的
|
|
294
|
+
runtime manifest 与选中的项目文件;输出参数必须是新目录,而不是单个
|
|
295
|
+
Dockerfile 文件路径
|
|
292
296
|
- `up`
|
|
293
297
|
- 支持 `--wait`、`--image`、`--base-image`、`--postgres-uri`、
|
|
294
298
|
`--recreate`、`--no-recreate`
|
|
299
|
+
- `--pass-env NAME` 显式选择一个已解析的应用变量,通过名称继承方式交给
|
|
300
|
+
直接 Docker carrier,变量值不会进入 argv
|
|
301
|
+
- `--compose-pass-env NAME` 显式选择一个已解析的应用变量交给 Compose;
|
|
302
|
+
配置文件中的 `compose_env` 提供同样的选择能力
|
|
295
303
|
|
|
296
304
|
部分仿照 LangGraph CLI 的参数会为了命令兼容性被解析,但当对应运行时
|
|
297
305
|
行为还未实现时会被直接拒绝。对于 mock、内存或 tunnel 化的本地工作流,
|
|
@@ -371,6 +379,35 @@ Redis 实例同时运行。
|
|
|
371
379
|
- `http.disable_a2a`:关闭 A2A 端点及 agent-card 发现路由
|
|
372
380
|
- `base_image`、`python_version`、`image_distro`、`pip_config_file`、
|
|
373
381
|
`dockerfile_lines`:Docker 构建自定义字段
|
|
382
|
+
- `build_include`:额外复制到净化构建 bundle 中的受信任普通文件或目录树
|
|
383
|
+
- `compose_env`:允许通过显式 Compose dotenv carrier 的、已完成解析的应用
|
|
384
|
+
环境变量名称
|
|
385
|
+
|
|
386
|
+
### 0.3.0 容器迁移
|
|
387
|
+
|
|
388
|
+
`preloaded-v1` 是不兼容旧行为、失败即关闭的容器边界。宿主机只解析一次
|
|
389
|
+
应用配置。从生成镜像启动的容器只接收显式选择的运行时 payload;宿主机环境、dotenv
|
|
390
|
+
文件、包仓库凭证和未选择的 Compose 值都不会进入构建上下文或镜像层。
|
|
391
|
+
`--pass-env` 与 `--compose-pass-env` 只应接收可信输入:它们授权变量跨越指定
|
|
392
|
+
运行时边界,并不允许变量进入镜像构建。
|
|
393
|
+
|
|
394
|
+
`build_include` 同样属于可信输入声明,必须审查每条路径。带凭证的 Python
|
|
395
|
+
仓库配置应使用 `pip_config_file`,CLI 会将其作为 BuildKit pip secret 挂载,
|
|
396
|
+
不会复制到上下文。`dockerfile` 命令现在输出 Docker 实际消费的完整 bundle
|
|
397
|
+
目录,而不是单独的 Dockerfile。
|
|
398
|
+
|
|
399
|
+
自定义镜像必须提供以下四个精确标签:
|
|
400
|
+
|
|
401
|
+
- `org.agentseek.environment-contract=preloaded-v1`
|
|
402
|
+
- `org.agentseek.runtime-manifest=/opt/agentseek/manifest.v1.json`
|
|
403
|
+
- `org.agentseek.runtime-distribution=agentseek-api`
|
|
404
|
+
- `org.agentseek.runtime-version=0.3.0`
|
|
405
|
+
|
|
406
|
+
manifest、已安装 distribution、entrypoint 与标签必须一致。系统不提供旧镜像
|
|
407
|
+
回退:传给 `up --image` 前必须完成迁移与校验;否则应继续用旧 launcher 配合
|
|
408
|
+
旧镜像。Train A 已达成版本为 API 0.2.3、templates 0.1.3、AgentSeek 0.1.3;
|
|
409
|
+
后续 Train B 的 template/catalog 计划单独发布 0.1.4。AgentSeek 也计划另行发布
|
|
410
|
+
0.1.4;两者都基于已经发布的 0.1.3。
|
|
374
411
|
|
|
375
412
|
CLI 层会尽量容忍 LangGraph 在端点级别使用的配置键,例如 `http` 与
|
|
376
413
|
`api_version`。Store 配置会被 HTTP Store API 以及注入的 LangGraph
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "agentseek-api"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.3.0"
|
|
4
4
|
description = "AgentSeek API core runtime with OceanBase checkpoints."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.12"
|
|
@@ -27,6 +27,7 @@ dependencies = [
|
|
|
27
27
|
"mcp>=1.27.1,<2",
|
|
28
28
|
"python-dotenv>=1.0,<1.3",
|
|
29
29
|
"scalar-fastapi>=1.0.3",
|
|
30
|
+
"packaging>=24.0",
|
|
30
31
|
]
|
|
31
32
|
|
|
32
33
|
[project.optional-dependencies]
|
|
@@ -53,6 +54,9 @@ dev = [
|
|
|
53
54
|
requires = ["hatchling"]
|
|
54
55
|
build-backend = "hatchling.build"
|
|
55
56
|
|
|
57
|
+
[tool.hatch.build]
|
|
58
|
+
exclude = ["/.superpowers"]
|
|
59
|
+
|
|
56
60
|
[tool.hatch.build.targets.wheel]
|
|
57
61
|
packages = ["src/agentseek_api"]
|
|
58
62
|
|
|
@@ -60,5 +64,6 @@ packages = ["src/agentseek_api"]
|
|
|
60
64
|
asyncio_mode = "auto"
|
|
61
65
|
testpaths = ["tests"]
|
|
62
66
|
markers = [
|
|
67
|
+
"docker: tests requiring Docker or Docker Compose",
|
|
63
68
|
"e2e: end-to-end tests against a live server and real SeekDB/OceanBase backend",
|
|
64
69
|
]
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Fail-closed structural scanning for Docker-save and OCI image archives."""
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import gzip
|
|
8
|
+
import hashlib
|
|
9
|
+
import io
|
|
10
|
+
import json
|
|
11
|
+
import os
|
|
12
|
+
import posixpath
|
|
13
|
+
import tarfile
|
|
14
|
+
from collections.abc import Mapping
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ImageArchiveError(RuntimeError):
|
|
20
|
+
"""A value-free image archive validation failure."""
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
_OCI_CONFIG_MEDIA_TYPES = {
|
|
24
|
+
"application/vnd.oci.image.config.v1+json",
|
|
25
|
+
"application/vnd.docker.container.image.v1+json",
|
|
26
|
+
}
|
|
27
|
+
_OCI_MANIFEST_MEDIA_TYPES = {
|
|
28
|
+
"application/vnd.oci.image.manifest.v1+json",
|
|
29
|
+
"application/vnd.docker.distribution.manifest.v2+json",
|
|
30
|
+
}
|
|
31
|
+
_OCI_LAYER_ENCODINGS = {
|
|
32
|
+
"application/vnd.oci.image.layer.v1.tar": "tar",
|
|
33
|
+
"application/vnd.oci.image.layer.v1.tar+gzip": "gzip",
|
|
34
|
+
"application/vnd.oci.image.layer.v1.tar+zstd": "zstd",
|
|
35
|
+
"application/vnd.docker.image.rootfs.diff.tar": "tar",
|
|
36
|
+
"application/vnd.docker.image.rootfs.diff.tar.gzip": "gzip",
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _safe_name(name: str) -> bool:
|
|
41
|
+
return (
|
|
42
|
+
bool(name)
|
|
43
|
+
and not name.startswith("/")
|
|
44
|
+
and posixpath.normpath(name) == name
|
|
45
|
+
and not any(part in {"", ".", ".."} for part in name.split("/"))
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def _canonical_tar_name(name: str) -> str | None:
|
|
50
|
+
if not name or "\x00" in name or name.startswith("/"):
|
|
51
|
+
return None
|
|
52
|
+
trimmed = name.rstrip("/")
|
|
53
|
+
if any(part == ".." for part in trimmed.split("/")):
|
|
54
|
+
return None
|
|
55
|
+
normalized = posixpath.normpath(trimmed)
|
|
56
|
+
if normalized == ".." or normalized.startswith("../"):
|
|
57
|
+
return None
|
|
58
|
+
return normalized
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _json_object(payload: bytes, boundary: str) -> Mapping[str, Any]:
|
|
62
|
+
try:
|
|
63
|
+
value = json.loads(payload)
|
|
64
|
+
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
|
|
65
|
+
raise ImageArchiveError(f"{boundary} JSON was malformed") from exc
|
|
66
|
+
if not isinstance(value, dict):
|
|
67
|
+
raise ImageArchiveError(f"{boundary} JSON shape was invalid")
|
|
68
|
+
return value
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _tar_files(payload: bytes, boundary: str) -> dict[str, bytes]:
|
|
72
|
+
files: dict[str, bytes] = {}
|
|
73
|
+
names: set[str] = set()
|
|
74
|
+
try:
|
|
75
|
+
with tarfile.open(fileobj=io.BytesIO(payload), mode="r:") as archive:
|
|
76
|
+
for member in archive:
|
|
77
|
+
name = _canonical_tar_name(member.name)
|
|
78
|
+
if name is None or (name == "." and not member.isdir()):
|
|
79
|
+
raise ImageArchiveError(f"{boundary} member path was unsafe")
|
|
80
|
+
if name == ".":
|
|
81
|
+
continue
|
|
82
|
+
if name in names:
|
|
83
|
+
raise ImageArchiveError(f"{boundary} contained duplicate members")
|
|
84
|
+
names.add(name)
|
|
85
|
+
if member.isfile():
|
|
86
|
+
stream = archive.extractfile(member)
|
|
87
|
+
if stream is None:
|
|
88
|
+
raise ImageArchiveError(f"{boundary} member was unreadable")
|
|
89
|
+
files[name] = stream.read()
|
|
90
|
+
except ImageArchiveError:
|
|
91
|
+
raise
|
|
92
|
+
except (tarfile.TarError, OSError, EOFError) as exc:
|
|
93
|
+
raise ImageArchiveError(f"{boundary} was malformed") from exc
|
|
94
|
+
return files
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _require_file(
|
|
98
|
+
files: Mapping[str, bytes], reference: object, boundary: str
|
|
99
|
+
) -> bytes:
|
|
100
|
+
if not isinstance(reference, str) or not _safe_name(reference):
|
|
101
|
+
raise ImageArchiveError(f"{boundary} reference path was unsafe")
|
|
102
|
+
if reference not in files:
|
|
103
|
+
raise ImageArchiveError(f"{boundary} reference was missing")
|
|
104
|
+
return files[reference]
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _scan_forbidden(payload: bytes, forbidden: bytes) -> None:
|
|
108
|
+
if not forbidden:
|
|
109
|
+
raise ImageArchiveError("forbidden byte sequence was empty")
|
|
110
|
+
if forbidden in payload:
|
|
111
|
+
raise ImageArchiveError("image surface contained forbidden bytes")
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _decode_layer(payload: bytes, encoding: str | None = None) -> bytes:
|
|
115
|
+
if encoding is None:
|
|
116
|
+
if payload.startswith(b"\x1f\x8b"):
|
|
117
|
+
encoding = "gzip"
|
|
118
|
+
elif payload.startswith(b"\x28\xb5\x2f\xfd"):
|
|
119
|
+
encoding = "zstd"
|
|
120
|
+
else:
|
|
121
|
+
encoding = "tar"
|
|
122
|
+
if encoding == "gzip":
|
|
123
|
+
try:
|
|
124
|
+
return gzip.decompress(payload)
|
|
125
|
+
except (OSError, EOFError) as exc:
|
|
126
|
+
raise ImageArchiveError("image layer compression was malformed") from exc
|
|
127
|
+
if encoding == "zstd":
|
|
128
|
+
try:
|
|
129
|
+
import zstandard
|
|
130
|
+
except ImportError as exc:
|
|
131
|
+
raise ImageArchiveError("zstd layer support was unavailable") from exc
|
|
132
|
+
try:
|
|
133
|
+
return zstandard.ZstdDecompressor().decompress(payload)
|
|
134
|
+
except zstandard.ZstdError as exc:
|
|
135
|
+
raise ImageArchiveError("image layer compression was malformed") from exc
|
|
136
|
+
if encoding == "tar":
|
|
137
|
+
return payload
|
|
138
|
+
raise ImageArchiveError("image layer compression was unsupported")
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _scan_layer(
|
|
142
|
+
payload: bytes, forbidden: bytes, *, encoding: str | None = None
|
|
143
|
+
) -> None:
|
|
144
|
+
decoded = _decode_layer(payload, encoding)
|
|
145
|
+
_scan_forbidden(decoded, forbidden)
|
|
146
|
+
for member_payload in _tar_files(decoded, "image layer").values():
|
|
147
|
+
_scan_forbidden(member_payload, forbidden)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _scan_docker_save(files: Mapping[str, bytes], forbidden: bytes) -> None:
|
|
151
|
+
try:
|
|
152
|
+
manifest = json.loads(
|
|
153
|
+
_require_file(files, "manifest.json", "Docker save manifest")
|
|
154
|
+
)
|
|
155
|
+
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
|
|
156
|
+
raise ImageArchiveError("Docker save manifest JSON was malformed") from exc
|
|
157
|
+
if (
|
|
158
|
+
not isinstance(manifest, list)
|
|
159
|
+
or len(manifest) != 1
|
|
160
|
+
or not isinstance(manifest[0], dict)
|
|
161
|
+
):
|
|
162
|
+
raise ImageArchiveError("Docker save manifest shape was unsupported")
|
|
163
|
+
entry = manifest[0]
|
|
164
|
+
config_reference = entry.get("Config")
|
|
165
|
+
layer_references = entry.get("Layers")
|
|
166
|
+
if not isinstance(layer_references, list) or not layer_references:
|
|
167
|
+
raise ImageArchiveError("Docker save layer references were invalid")
|
|
168
|
+
if (
|
|
169
|
+
not isinstance(config_reference, str)
|
|
170
|
+
or any(not isinstance(reference, str) for reference in layer_references)
|
|
171
|
+
or config_reference in layer_references
|
|
172
|
+
):
|
|
173
|
+
raise ImageArchiveError("Docker save references were invalid")
|
|
174
|
+
config = _require_file(files, config_reference, "Docker save config")
|
|
175
|
+
_json_object(config, "Docker save config")
|
|
176
|
+
_scan_forbidden(config, forbidden)
|
|
177
|
+
for reference in dict.fromkeys(layer_references):
|
|
178
|
+
_scan_layer(_require_file(files, reference, "Docker save layer"), forbidden)
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
def _oci_blob(
|
|
182
|
+
files: Mapping[str, bytes],
|
|
183
|
+
descriptor: object,
|
|
184
|
+
media_types: set[str],
|
|
185
|
+
boundary: str,
|
|
186
|
+
) -> tuple[bytes, str]:
|
|
187
|
+
if not isinstance(descriptor, dict):
|
|
188
|
+
raise ImageArchiveError(f"{boundary} descriptor shape was invalid")
|
|
189
|
+
digest = descriptor.get("digest")
|
|
190
|
+
size = descriptor.get("size")
|
|
191
|
+
media_type = descriptor.get("mediaType")
|
|
192
|
+
if (
|
|
193
|
+
not isinstance(digest, str)
|
|
194
|
+
or not digest.startswith("sha256:")
|
|
195
|
+
or len(digest) != 71
|
|
196
|
+
):
|
|
197
|
+
raise ImageArchiveError(f"{boundary} digest was invalid")
|
|
198
|
+
hexadecimal = digest.removeprefix("sha256:")
|
|
199
|
+
if any(character not in "0123456789abcdef" for character in hexadecimal):
|
|
200
|
+
raise ImageArchiveError(f"{boundary} digest was invalid")
|
|
201
|
+
if not isinstance(size, int) or size < 0 or media_type not in media_types:
|
|
202
|
+
raise ImageArchiveError(f"{boundary} descriptor was unsupported")
|
|
203
|
+
payload = _require_file(files, f"blobs/sha256/{hexadecimal}", boundary)
|
|
204
|
+
if len(payload) != size or hashlib.sha256(payload).hexdigest() != hexadecimal:
|
|
205
|
+
raise ImageArchiveError(f"{boundary} blob integrity failed")
|
|
206
|
+
return payload, media_type
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def _scan_oci(files: Mapping[str, bytes], forbidden: bytes) -> None:
|
|
210
|
+
layout = _json_object(
|
|
211
|
+
_require_file(files, "oci-layout", "OCI layout"), "OCI layout"
|
|
212
|
+
)
|
|
213
|
+
if layout.get("imageLayoutVersion") != "1.0.0":
|
|
214
|
+
raise ImageArchiveError("OCI layout version was unsupported")
|
|
215
|
+
index = _json_object(_require_file(files, "index.json", "OCI index"), "OCI index")
|
|
216
|
+
manifests = index.get("manifests")
|
|
217
|
+
if not isinstance(manifests, list) or len(manifests) != 1:
|
|
218
|
+
raise ImageArchiveError("OCI index manifest selection was unsupported")
|
|
219
|
+
manifest_bytes, _ = _oci_blob(
|
|
220
|
+
files, manifests[0], _OCI_MANIFEST_MEDIA_TYPES, "OCI manifest"
|
|
221
|
+
)
|
|
222
|
+
manifest = _json_object(manifest_bytes, "OCI manifest")
|
|
223
|
+
config_descriptor = manifest.get("config")
|
|
224
|
+
config_bytes, _ = _oci_blob(
|
|
225
|
+
files, config_descriptor, _OCI_CONFIG_MEDIA_TYPES, "OCI config"
|
|
226
|
+
)
|
|
227
|
+
_json_object(config_bytes, "OCI config")
|
|
228
|
+
_scan_forbidden(config_bytes, forbidden)
|
|
229
|
+
layers = manifest.get("layers")
|
|
230
|
+
if not isinstance(layers, list) or not layers:
|
|
231
|
+
raise ImageArchiveError("OCI layer descriptors were invalid")
|
|
232
|
+
config_digest = (
|
|
233
|
+
config_descriptor.get("digest") if isinstance(config_descriptor, dict) else None
|
|
234
|
+
)
|
|
235
|
+
descriptors: dict[str, Mapping[str, Any]] = {}
|
|
236
|
+
for descriptor in layers:
|
|
237
|
+
if not isinstance(descriptor, dict):
|
|
238
|
+
raise ImageArchiveError("OCI layer references were invalid")
|
|
239
|
+
digest = descriptor.get("digest")
|
|
240
|
+
if not isinstance(digest, str) or digest == config_digest:
|
|
241
|
+
raise ImageArchiveError("OCI layer references were invalid")
|
|
242
|
+
previous = descriptors.get(digest)
|
|
243
|
+
if previous is not None:
|
|
244
|
+
if descriptor != previous:
|
|
245
|
+
raise ImageArchiveError("OCI layer references conflicted")
|
|
246
|
+
continue
|
|
247
|
+
descriptors[digest] = descriptor
|
|
248
|
+
layer_bytes, media_type = _oci_blob(
|
|
249
|
+
files, descriptor, set(_OCI_LAYER_ENCODINGS), "OCI layer"
|
|
250
|
+
)
|
|
251
|
+
_scan_layer(layer_bytes, forbidden, encoding=_OCI_LAYER_ENCODINGS[media_type])
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def scan_image_archive(
|
|
255
|
+
archive: bytes | Path, *, forbidden: bytes, history: bytes
|
|
256
|
+
) -> None:
|
|
257
|
+
"""Validate and scan every referenced image surface without exposing values."""
|
|
258
|
+
|
|
259
|
+
try:
|
|
260
|
+
payload = archive.read_bytes() if isinstance(archive, Path) else bytes(archive)
|
|
261
|
+
except OSError as exc:
|
|
262
|
+
raise ImageArchiveError("image archive could not be read") from exc
|
|
263
|
+
files = _tar_files(payload, "image archive")
|
|
264
|
+
_scan_forbidden(history, forbidden)
|
|
265
|
+
recognized = False
|
|
266
|
+
if "manifest.json" in files:
|
|
267
|
+
_scan_docker_save(files, forbidden)
|
|
268
|
+
recognized = True
|
|
269
|
+
if "oci-layout" in files or "index.json" in files:
|
|
270
|
+
_scan_oci(files, forbidden)
|
|
271
|
+
recognized = True
|
|
272
|
+
if not recognized:
|
|
273
|
+
raise ImageArchiveError("image archive format was unsupported")
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def main(argv: list[str] | None = None) -> int:
|
|
277
|
+
parser = argparse.ArgumentParser()
|
|
278
|
+
parser.add_argument("--archive", type=Path, required=True)
|
|
279
|
+
parser.add_argument("--history", type=Path, required=True)
|
|
280
|
+
args = parser.parse_args(argv)
|
|
281
|
+
sentinel = os.environ.get("AGENTSEEK_IMAGE_SCAN_SENTINEL")
|
|
282
|
+
if not sentinel:
|
|
283
|
+
raise SystemExit("image archive sentinel was unavailable")
|
|
284
|
+
try:
|
|
285
|
+
scan_image_archive(
|
|
286
|
+
args.archive,
|
|
287
|
+
forbidden=sentinel.encode(),
|
|
288
|
+
history=args.history.read_bytes(),
|
|
289
|
+
)
|
|
290
|
+
except (ImageArchiveError, OSError):
|
|
291
|
+
raise SystemExit("image archive boundary verification failed") from None
|
|
292
|
+
return 0
|
|
293
|
+
|
|
294
|
+
|
|
295
|
+
if __name__ == "__main__":
|
|
296
|
+
raise SystemExit(main())
|