imbi-api 2.41.0__tar.gz → 2.43.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.
- {imbi_api-2.41.0 → imbi_api-2.43.0}/PKG-INFO +3 -4
- {imbi_api-2.41.0 → imbi_api-2.43.0}/pyproject.toml +3 -4
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/agent_tasks/__init__.py +2 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/agent_tasks/store.py +88 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/agent_api_routes.py +1 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/seed.py +7 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/commit_sync/service.py +35 -3
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/agent_tasks.py +43 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/agents.py +90 -25
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/harness.py +48 -40
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/project_deployments.py +505 -82
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/projects.py +95 -23
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/secret_broker.py +10 -3
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/harness/marketplaces.py +44 -67
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/identity/state.py +3 -4
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/maintenance/registry.py +4 -1
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/secret_broker/__init__.py +1 -4
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/secret_broker/broker.py +27 -58
- imbi_api-2.43.0/src/imbi/api/secret_broker/providers.py +287 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_agent_platform.py +2 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_agent_task_log.py +32 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_agent_task_relations.py +14 -2
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_agent_tasks.py +132 -3
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_agents.py +119 -12
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_github_app_repository.py +12 -14
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_project_deployments.py +958 -15
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_projects.py +161 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_secret_broker.py +1 -1
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/secret_support.py +22 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_agent_platform.py +105 -17
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_commit_sync_service.py +49 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_projects_drift_ranges.py +56 -1
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_secret_broker.py +0 -3
- imbi_api-2.43.0/tests/test_secret_github_app.py +250 -0
- imbi_api-2.43.0/tests/test_secret_providers.py +137 -0
- imbi_api-2.41.0/src/imbi/api/auth/oauth.py +0 -496
- imbi_api-2.41.0/src/imbi/api/secret_broker/github_app.py +0 -747
- imbi_api-2.41.0/src/imbi/api/secret_broker/providers.py +0 -765
- imbi_api-2.41.0/tests/auth/test_oauth.py +0 -1110
- imbi_api-2.41.0/tests/test_secret_github_app.py +0 -749
- imbi_api-2.41.0/tests/test_secret_github_app_live.py +0 -156
- imbi_api-2.41.0/tests/test_secret_providers.py +0 -376
- {imbi_api-2.41.0 → imbi_api-2.43.0}/.gitignore +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/README.md +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/moon.yml +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/README.md +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/__init__.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/agent_tasks/log.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/agent_tasks/schemata.toml +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/agent_tasks/sweeper.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/agent_tools.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/app.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/__init__.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/agent_api.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/authorization_codes.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/autonomous.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/internal_services.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/local_auth.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/login_providers.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/membership.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/models.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/oauth_clients.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/organizations.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/password.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/permissions.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/principals.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/sessions.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/tokens.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/auth/totp.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/backfill_embeddings.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/blueprint_attributes.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/blueprint_compliance.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/commit_pushed_at_repair.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/commit_sync/__init__.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/commit_sync/queue.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/component_facts.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/deployment_status_repair.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/deployment_sweeper.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/deployment_sync/__init__.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/deployment_sync/queue.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/deployment_sync/service.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/documents/__init__.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/documents/read_sweeper.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/domain/__init__.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/domain/models.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/domain/scoring.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/drift.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/email/__init__.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/email/client.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/email/dependencies.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/email/models.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/email/templates/base.html +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/email/templates/base.txt +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/email/templates/password_reset.html +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/email/templates/password_reset.txt +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/email/templates/welcome.html +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/email/templates/welcome.txt +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/email/templates.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/__init__.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/_credentials.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/_document_events.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/_document_history.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/_document_reads.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/_document_scope.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/_helpers.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/_json_fields.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/_pagination.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/_request_urls.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/_search_index.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/admin.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/admin_plugins.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/agent_platform.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/agent_task_harness.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/agent_usage.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/ai_models.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/ai_providers.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/api_keys.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/auth.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/auth_providers.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/blueprints.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/client_credentials.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/comments.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/components.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/dashboard.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/document_analytics.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/document_likes.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/document_presence.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/document_templates.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/document_versions.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/documents.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/environments.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/events.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/graph_query.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/iggy_connectors.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/integrations.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/link_definitions.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/local_auth.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/maintenance.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/mcp_servers.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/mfa.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/oauth_metadata.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/operations_log.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/organizations.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/plugin_edges.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/plugin_entities.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/plugins.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/project_analysis.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/project_commit_sync.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/project_configuration.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/project_incidents.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/project_integrations.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/project_lifecycle.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/project_logs.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/project_plugins.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/project_pr_sync.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/project_types.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/prompts.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/pull_requests.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/releases.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/roles.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/sa_api_keys.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/scoring.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/scoring_policies.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/search.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/service_accounts.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/status.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/tags.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/teams.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/uploads.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/user_activity.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/users.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/webhooks.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/workflow_runs.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/endpoints/workflows.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/entrypoint.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/graph_ids.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/graph_sql.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/harness/__init__.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/harness/auth.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/harness/egress.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/harness/metrics.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/harness/platform.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/harness/schemata.toml +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/harness/store.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/harness/sweeper.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/harness/tokens.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/identity/__init__.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/identity/attribution.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/identity/endpoints.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/identity/errors.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/identity/flows.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/identity/host_integration.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/identity/models.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/identity/repository.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/identity/resolution.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/identity/sweeper.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/iggy_metrics.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/lifespans.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/llm/__init__.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/llm/dependencies.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/maintenance/__init__.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/maintenance/log.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/maintenance/operations.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/maintenance/state.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/maintenance/worker.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/mcp_test.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/middleware/__init__.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/middleware/rate_limit.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/models.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/openapi.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/orphan_releases.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/plugins/__init__.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/plugins/assignment_writer.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/plugins/assignments.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/plugins/credentials.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/plugins/installer.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/plugins/lifecycle.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/plugins/lifecycle_dispatch.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/plugins/reload.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/plugins/resolution.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/plugins/schemas.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/pr_sync/__init__.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/pr_sync/queue.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/pr_sync/service.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/prompts/__init__.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/prompts/release_notes_system.md +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/prompts/seed.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/py.typed +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/relationships.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/release_promote/__init__.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/release_promote/queue.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/release_promote/service.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/sbom.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/scoring/__init__.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/scoring/queue.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/secret_broker/audit.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/secret_broker/schemata.toml +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/secret_broker/store.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/settings.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/storage/__init__.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/storage/client.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/storage/dependencies.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/storage/thumbnails.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/storage/validation.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/user_document_orgs.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/workflows/__init__.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/workflows/runs.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/workflows/runtime_schemata.toml +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/workflows/schemata.toml +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/workflows/store.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/src/imbi/api/workflows/sweeper.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/__init__.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/auth/__init__.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/auth/test_authentication.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/auth/test_authorization.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/auth/test_encryption.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/auth/test_internal_services.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/auth/test_login_providers.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/auth/test_membership.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/auth/test_oauth_server.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/auth/test_organizations.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/auth/test_permissions.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/auth/test_seed.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/auth/test_sessions.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/auth/test_totp.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/clickhouse/__init__.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/clickhouse/test_client.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/clickhouse/test_init.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/clickhouse/test_privacy.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/email/__init__.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/email/test_audit_clickhouse.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/email/test_client.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/email/test_init.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/email/test_integration.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/email/test_models.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/email/test_templates.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/__init__.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_admin.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_admin_plugins.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_agent_api.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_agent_task_actions.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_agent_task_harness.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_agent_usage.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_ai_models.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_ai_providers.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_api_keys.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_auth.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_auth_providers.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_blueprints.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_client_credentials.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_comments.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_components.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_credentials.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_cross_org_reads.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_dashboard.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_document_analytics.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_document_likes.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_document_presence.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_document_reads.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_document_reads_sql.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_document_templates.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_document_versions.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_documents.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_environments.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_events.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_graph_query.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_harness.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_harness_egress.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_harness_secrets.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_iggy_connectors.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_init.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_integrations.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_json_fields.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_link_definitions.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_local_auth.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_maintenance.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_mcp_servers.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_mfa.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_operations_log.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_organizations.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_pagination.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_plugin_label_validation.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_plugins.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_project_analysis.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_project_commit_sync.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_project_configuration.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_project_deployments_autonomous.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_project_incidents.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_project_integrations.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_project_lifecycle.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_project_logs.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_project_plugins.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_project_response_surfacing.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_project_types.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_projects_helpers.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_prompt_decisions.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_prompts.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_pull_requests.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_releases.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_request_urls.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_roles.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_sa_api_keys.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_scoring.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_scoring_policies.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_search.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_search_index.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_service_accounts.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_service_writeback.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_status.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_tags.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_teams.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_uploads.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_user_activity.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_users.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_webhooks.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_workflow_run_history.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_workflow_runs.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_workflow_triggers.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/endpoints/test_workflows.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/fixtures/marketplaces/claude-code.json +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/fixtures/sbom/npm-realistic.json +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/fixtures/sbom/pypi-realistic.json +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/fixtures/sbom/tiny.json +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/identity/__init__.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/identity/test_endpoints.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/identity/test_errors.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/identity/test_flows.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/identity/test_host_integration.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/identity/test_repository.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/identity/test_resolution.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/identity/test_state.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/identity/test_sweeper.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/middleware/test_rate_limit.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/storage/__init__.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/storage/test_client.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/storage/test_init.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/storage/test_thumbnails.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/storage/test_validation.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/support.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_agent_task_events_e2e.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_agent_tools.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_app.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_assignment_writer.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_auth_settings.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_backfill_embeddings.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_blueprint_attributes.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_blueprint_compliance.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_blueprints.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_commit_pushed_at_repair.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_commit_sync_queue.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_component_facts.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_deployment_status_repair.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_deployment_sweeper.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_deployment_sync_queue.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_deployment_sync_service.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_drift.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_entrypoint.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_graph_ids.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_graph_sql.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_identity_attribution.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_iggy_metrics.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_init.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_installer.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_lifecycle_dispatch.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_lifespans.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_maintenance_log.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_maintenance_operations.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_maintenance_state.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_maintenance_worker.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_models.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_openapi.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_orphan_releases.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_plugin_schemas.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_plugins.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_plugins_assignments.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_plugins_credentials.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_pr_sync_queue.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_pr_sync_service.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_prompt_seed.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_pull_requests_pending_deploy.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_release_promote_service.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_sbom.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_scoring_queue.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_scoring_triggers.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_settings.py +0 -0
- {imbi_api-2.41.0 → imbi_api-2.43.0}/tests/test_workflow_reconcile.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: imbi-api
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.43.0
|
|
4
4
|
Summary: Imbi is a DevOps Service Management Platform designed to provide an efficient way to manage a large environment that contains many services and applications.
|
|
5
5
|
Author-email: Alex Campbell <alexc@aweber.com>, Dave Shawley <daves@aweber.com>, "Gavin M. Roy" <gavinr@aweber.com>
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -19,15 +19,14 @@ Requires-Dist: cyclonedx-python-lib<12,>=11.7
|
|
|
19
19
|
Requires-Dist: fastapi
|
|
20
20
|
Requires-Dist: filetype
|
|
21
21
|
Requires-Dist: httpx<1,>=0.28.1
|
|
22
|
-
Requires-Dist: imbi-common[databases,llm,mcp,sentry,server]==2.
|
|
23
|
-
Requires-Dist: imbi-plugin-github==2.41.0
|
|
22
|
+
Requires-Dist: imbi-common[databases,llm,mcp,sentry,server]==2.43.0
|
|
24
23
|
Requires-Dist: jinja2
|
|
25
24
|
Requires-Dist: jsonpatch>=1.33
|
|
26
25
|
Requires-Dist: limits<6,>=5
|
|
27
26
|
Requires-Dist: mcp>=1.26.0
|
|
28
27
|
Requires-Dist: nanoid>=2.0.0
|
|
29
28
|
Requires-Dist: orjson>=3.11.6
|
|
30
|
-
Requires-Dist: pillow
|
|
29
|
+
Requires-Dist: pillow>=13.0.0
|
|
31
30
|
Requires-Dist: pydantic[email]
|
|
32
31
|
Requires-Dist: pyotp>=2.9.0
|
|
33
32
|
Requires-Dist: python-multipart>=0.0.26
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "imbi-api"
|
|
3
|
-
version = "2.
|
|
3
|
+
version = "2.43.0"
|
|
4
4
|
description = "Imbi is a DevOps Service Management Platform designed to provide an efficient way to manage a large environment that contains many services and applications."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.14"
|
|
@@ -19,7 +19,7 @@ classifiers = [
|
|
|
19
19
|
"Programming Language :: Python :: 3.14",
|
|
20
20
|
]
|
|
21
21
|
dependencies = [
|
|
22
|
-
"Pillow",
|
|
22
|
+
"Pillow>=13.0.0",
|
|
23
23
|
"aioboto3>=15.5.0",
|
|
24
24
|
"aiohttp",
|
|
25
25
|
"argon2-cffi>=25.1.0",
|
|
@@ -28,8 +28,7 @@ dependencies = [
|
|
|
28
28
|
"fastapi",
|
|
29
29
|
"filetype",
|
|
30
30
|
"httpx>=0.28.1,<1",
|
|
31
|
-
"imbi-common[databases,llm,mcp,sentry,server]==2.
|
|
32
|
-
"imbi-plugin-github==2.41.0",
|
|
31
|
+
"imbi-common[databases,llm,mcp,sentry,server]==2.43.0",
|
|
33
32
|
"jinja2",
|
|
34
33
|
"jsonpatch>=1.33",
|
|
35
34
|
"limits>=5,<6",
|
|
@@ -42,6 +42,7 @@ from imbi.api.agent_tasks.store import (
|
|
|
42
42
|
TaskArchived,
|
|
43
43
|
TaskClosed,
|
|
44
44
|
TaskNotFound,
|
|
45
|
+
TaskOpen,
|
|
45
46
|
TaskStore,
|
|
46
47
|
Usage,
|
|
47
48
|
)
|
|
@@ -124,6 +125,7 @@ __all__ = [
|
|
|
124
125
|
'TaskArchived',
|
|
125
126
|
'TaskClosed',
|
|
126
127
|
'TaskNotFound',
|
|
128
|
+
'TaskOpen',
|
|
127
129
|
'TaskStore',
|
|
128
130
|
'Usage',
|
|
129
131
|
'create_pool',
|
|
@@ -142,6 +142,10 @@ class TaskClosed(ValueError):
|
|
|
142
142
|
"""The task is closed. A closed task does not change."""
|
|
143
143
|
|
|
144
144
|
|
|
145
|
+
class TaskOpen(ValueError):
|
|
146
|
+
"""The task, or a task that it delegated to, is not closed."""
|
|
147
|
+
|
|
148
|
+
|
|
145
149
|
class CancelPending(ValueError):
|
|
146
150
|
"""A cancel waits for the harness; the control cannot change."""
|
|
147
151
|
|
|
@@ -454,10 +458,24 @@ class TaskStore:
|
|
|
454
458
|
Return the task and ``True``. When a concurrent request made a
|
|
455
459
|
task with the same idempotency key first, return that task and
|
|
456
460
|
``False``. The rollback also returns the short id to the counter.
|
|
461
|
+
|
|
462
|
+
Raises:
|
|
463
|
+
TaskClosed: The origin is a task that is closed or gone.
|
|
464
|
+
|
|
457
465
|
"""
|
|
458
466
|
task_id = uuid.uuid4()
|
|
459
467
|
try:
|
|
460
468
|
async with self._pool.connection() as conn, conn.transaction():
|
|
469
|
+
if task.origin_kind == 'task':
|
|
470
|
+
# The lock conflicts with the lock in delete, so a
|
|
471
|
+
# parent cannot go while its child is made.
|
|
472
|
+
cursor = await conn.execute(
|
|
473
|
+
'SELECT 1 FROM agent_runtime.tasks WHERE id = %s::uuid'
|
|
474
|
+
" AND status <> 'closed' FOR KEY SHARE",
|
|
475
|
+
(task.origin_id,),
|
|
476
|
+
)
|
|
477
|
+
if await cursor.fetchone() is None:
|
|
478
|
+
raise TaskClosed(task.origin_id)
|
|
461
479
|
short_id = await _next_short_id(conn, task.organization_id)
|
|
462
480
|
await conn.execute(
|
|
463
481
|
'INSERT INTO agent_runtime.tasks (id, organization_id,'
|
|
@@ -709,6 +727,76 @@ class TaskStore:
|
|
|
709
727
|
)
|
|
710
728
|
return True
|
|
711
729
|
|
|
730
|
+
async def delete(self, organization_id: str, short_id: str) -> Row:
|
|
731
|
+
"""Delete a closed task and return its row.
|
|
732
|
+
|
|
733
|
+
Its events, sessions, requests, budget ledger, and harness rows
|
|
734
|
+
go by cascade; its dependency and project rows go here. The
|
|
735
|
+
secret resolutions and the ClickHouse rows (archived events,
|
|
736
|
+
usage) stay, for the security and cost history.
|
|
737
|
+
|
|
738
|
+
Raises:
|
|
739
|
+
TaskNotFound: No such task.
|
|
740
|
+
TaskOpen: The task, or a child or chain task, is not closed,
|
|
741
|
+
the log of the task is not archived, its harness chain
|
|
742
|
+
is not done, or it has open secret resolutions.
|
|
743
|
+
|
|
744
|
+
"""
|
|
745
|
+
async with self._pool.connection() as conn, conn.transaction():
|
|
746
|
+
task = await _fetch_one(
|
|
747
|
+
conn,
|
|
748
|
+
'SELECT * FROM agent_runtime.tasks'
|
|
749
|
+
' WHERE organization_id = %s AND short_id = %s FOR UPDATE',
|
|
750
|
+
(organization_id, short_id),
|
|
751
|
+
)
|
|
752
|
+
if task is None:
|
|
753
|
+
raise TaskNotFound(short_id)
|
|
754
|
+
# The cascade would remove events and ledger rows that the
|
|
755
|
+
# sweep has not yet put in ClickHouse.
|
|
756
|
+
if task['log_archived_at'] is None:
|
|
757
|
+
raise TaskOpen(short_id)
|
|
758
|
+
# The harness chain row of a root task cascades, so an open
|
|
759
|
+
# task in its chain must not lose it. A legacy root has no
|
|
760
|
+
# stored chain_id.
|
|
761
|
+
chain_id = task['chain_id'] or task['id']
|
|
762
|
+
cursor = await conn.execute(
|
|
763
|
+
'SELECT 1 FROM agent_runtime.tasks'
|
|
764
|
+
" WHERE status <> 'closed' AND (id = %s OR chain_id = %s"
|
|
765
|
+
' OR (chain_id IS NULL AND id = %s)'
|
|
766
|
+
" OR (origin_kind = 'task' AND origin_id = %s)) LIMIT 1",
|
|
767
|
+
(task['id'], chain_id, chain_id, str(task['id'])),
|
|
768
|
+
)
|
|
769
|
+
if await cursor.fetchone() is not None:
|
|
770
|
+
raise TaskOpen(short_id)
|
|
771
|
+
# The lease sweep still needs the chain row of a root that
|
|
772
|
+
# waits for its fencers, and Broker.sweep joins the open
|
|
773
|
+
# resolutions of a failed close hook to the task.
|
|
774
|
+
cursor = await conn.execute(
|
|
775
|
+
'SELECT 1 FROM harness.chains WHERE root_task_id = %s'
|
|
776
|
+
" AND state IN ('running', 'fencing', 'stuck')"
|
|
777
|
+
' UNION ALL SELECT 1 FROM secrets.resolutions'
|
|
778
|
+
" WHERE task_id = %s AND state IN ('pending', 'active')"
|
|
779
|
+
' LIMIT 1',
|
|
780
|
+
(task['id'], task['id']),
|
|
781
|
+
)
|
|
782
|
+
if await cursor.fetchone() is not None:
|
|
783
|
+
raise TaskOpen(short_id)
|
|
784
|
+
await conn.execute(
|
|
785
|
+
'DELETE FROM agent_runtime.task_dependencies'
|
|
786
|
+
' WHERE organization_id = %s'
|
|
787
|
+
' AND (dependent_task_id = %s OR prerequisite_task_id = %s)',
|
|
788
|
+
(organization_id, task['id'], task['id']),
|
|
789
|
+
)
|
|
790
|
+
await conn.execute(
|
|
791
|
+
'DELETE FROM agent_runtime.task_projects'
|
|
792
|
+
' WHERE organization_id = %s AND task_id = %s',
|
|
793
|
+
(organization_id, task['id']),
|
|
794
|
+
)
|
|
795
|
+
await conn.execute(
|
|
796
|
+
'DELETE FROM agent_runtime.tasks WHERE id = %s', (task['id'],)
|
|
797
|
+
)
|
|
798
|
+
return task
|
|
799
|
+
|
|
712
800
|
async def set_control(
|
|
713
801
|
self,
|
|
714
802
|
organization_id: str,
|
|
@@ -244,6 +244,7 @@ REFUSED_ROUTES: frozenset[str] = frozenset(
|
|
|
244
244
|
'PUT /prompts/{namespace}/{slug}/labels/{label}',
|
|
245
245
|
'PUT /prompts/{namespace}/{slug}/versions/{n}/evaluation',
|
|
246
246
|
# Agent tasks: an agent works on its tasks through the harness routes only, so it cannot answer its own approval or question, reassign, or start another agent outside its chain
|
|
247
|
+
'DELETE /organizations/{org_slug}/agent-tasks/{short_id}',
|
|
247
248
|
'DELETE /organizations/{org_slug}/agent-tasks/{short_id}/projects/{project_id}',
|
|
248
249
|
'DELETE /organizations/{org_slug}/agent-tasks/{short_id}/requires/{prerequisite}',
|
|
249
250
|
'POST /organizations/{org_slug}/agent-tasks/',
|
|
@@ -484,6 +484,13 @@ STANDARD_PERMISSIONS: list[tuple[str, str, str, str]] = [
|
|
|
484
484
|
'resolve',
|
|
485
485
|
'Answer feedback requests and resolve approvals of agent tasks',
|
|
486
486
|
),
|
|
487
|
+
# Granted to no non-admin default role.
|
|
488
|
+
(
|
|
489
|
+
'agent_task:delete',
|
|
490
|
+
'agent_task',
|
|
491
|
+
'delete',
|
|
492
|
+
'Delete closed agent tasks',
|
|
493
|
+
),
|
|
487
494
|
# Workflows (ADR 0021)
|
|
488
495
|
(
|
|
489
496
|
'workflow:read',
|
|
@@ -144,9 +144,12 @@ async def run_sync(
|
|
|
144
144
|
:class:`CommitSyncUnavailable` when no integration provides the
|
|
145
145
|
capability; other failures propagate so the caller can record them.
|
|
146
146
|
|
|
147
|
-
Once the tags are in,
|
|
148
|
-
|
|
149
|
-
|
|
147
|
+
Once the tags are in, points every tagged ``Release`` node at the
|
|
148
|
+
commit its tag now names -- a tag re-promoted after a failed build
|
|
149
|
+
keeps its first commit on the node, and no other sync rewrites it.
|
|
150
|
+
Then enriches any that carry no release notes -- a tag cut outside
|
|
151
|
+
imbi has no ``Release`` node, so this is what gives it a title and
|
|
152
|
+
body to show. Then re-reads the drift notes ref, because
|
|
150
153
|
the commits this just recorded have no verdicts and readers fail
|
|
151
154
|
closed on that: recovering a commit without its verdict shows it as
|
|
152
155
|
drifted. Both are best-effort: a failure there is logged and leaves
|
|
@@ -164,6 +167,7 @@ async def run_sync(
|
|
|
164
167
|
)
|
|
165
168
|
handler = typing.cast('CommitSyncCapability', resolved.capability_cls())
|
|
166
169
|
counts = await handler.sync_all_history(ctx=ctx, credentials=credentials)
|
|
170
|
+
await _reconcile_release_committishes(db, org_slug, project_id)
|
|
167
171
|
await _backfill_release_notes(db, org_slug, project_id)
|
|
168
172
|
await _resync_drift_verdicts(db, org_slug, project_id)
|
|
169
173
|
return counts
|
|
@@ -203,6 +207,34 @@ async def run_tag_sync(
|
|
|
203
207
|
return await handler.sync_tag(ctx=ctx, credentials=credentials, tag=tag)
|
|
204
208
|
|
|
205
209
|
|
|
210
|
+
async def _reconcile_release_committishes(
|
|
211
|
+
db: graph.Graph, org_slug: str, project_id: str
|
|
212
|
+
) -> None:
|
|
213
|
+
"""Heal Release nodes whose synced tag names another commit."""
|
|
214
|
+
# Imported here (not at module load) so the worker/service module
|
|
215
|
+
# never pulls the endpoints package at import time.
|
|
216
|
+
from imbi.api.endpoints import project_deployments
|
|
217
|
+
|
|
218
|
+
try:
|
|
219
|
+
healed = await project_deployments.reconcile_release_committishes(
|
|
220
|
+
db, org_slug=org_slug, project_id=project_id
|
|
221
|
+
)
|
|
222
|
+
except Exception: # noqa: BLE001
|
|
223
|
+
LOGGER.warning(
|
|
224
|
+
'release committish reconcile failed for project %s',
|
|
225
|
+
project_id,
|
|
226
|
+
exc_info=True,
|
|
227
|
+
)
|
|
228
|
+
return
|
|
229
|
+
if healed:
|
|
230
|
+
LOGGER.info(
|
|
231
|
+
'commit-sync healed the committish of %d release(s) for '
|
|
232
|
+
'project %s',
|
|
233
|
+
healed,
|
|
234
|
+
project_id,
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
|
|
206
238
|
async def _backfill_release_notes(
|
|
207
239
|
db: graph.Graph, org_slug: str, project_id: str
|
|
208
240
|
) -> None:
|
|
@@ -1123,6 +1123,49 @@ async def cancel_agent_task(
|
|
|
1123
1123
|
return await _set_control(store, org_id, short_id, 'cancel', auth)
|
|
1124
1124
|
|
|
1125
1125
|
|
|
1126
|
+
@agent_tasks_router.delete('/{short_id}', status_code=204)
|
|
1127
|
+
async def delete_agent_task(
|
|
1128
|
+
org_id: OrgId,
|
|
1129
|
+
short_id: str,
|
|
1130
|
+
store: agent_tasks.Store,
|
|
1131
|
+
auth: typing.Annotated[
|
|
1132
|
+
permissions.AuthContext,
|
|
1133
|
+
fastapi.Depends(permissions.require_permission('agent_task:delete')),
|
|
1134
|
+
],
|
|
1135
|
+
) -> None:
|
|
1136
|
+
"""Delete a closed task, with its events and relations.
|
|
1137
|
+
|
|
1138
|
+
The secret audit and the usage records of the task stay.
|
|
1139
|
+
|
|
1140
|
+
Raises:
|
|
1141
|
+
403: The caller has no ``agent_task:delete``, or is not a member
|
|
1142
|
+
of the org.
|
|
1143
|
+
404: No such task.
|
|
1144
|
+
409: The task, or a task in its chain, is not closed.
|
|
1145
|
+
|
|
1146
|
+
"""
|
|
1147
|
+
try:
|
|
1148
|
+
task = await store.delete(org_id, short_id.upper())
|
|
1149
|
+
except agent_tasks.TaskNotFound as e:
|
|
1150
|
+
raise fastapi.HTTPException(
|
|
1151
|
+
status_code=404, detail=f'Task {short_id!r} not found'
|
|
1152
|
+
) from e
|
|
1153
|
+
except agent_tasks.TaskOpen as e:
|
|
1154
|
+
raise fastapi.HTTPException(
|
|
1155
|
+
status_code=409,
|
|
1156
|
+
detail=(
|
|
1157
|
+
f'Task {short_id!r} or a task in its chain is not closed,'
|
|
1158
|
+
' or its log is not archived yet'
|
|
1159
|
+
),
|
|
1160
|
+
) from e
|
|
1161
|
+
LOGGER.info(
|
|
1162
|
+
'Agent task %s (%s) deleted by %s',
|
|
1163
|
+
task['short_id'],
|
|
1164
|
+
task['id'],
|
|
1165
|
+
_actor(auth).id,
|
|
1166
|
+
)
|
|
1167
|
+
|
|
1168
|
+
|
|
1126
1169
|
@agent_tasks_router.post(
|
|
1127
1170
|
'/{short_id}/reassign', response_model=AgentTaskResponse
|
|
1128
1171
|
)
|
|
@@ -16,7 +16,8 @@ version, so the system prompt and the model come back too.
|
|
|
16
16
|
Each agent is its own service principal (ADR 0020): a
|
|
17
17
|
``ServiceAccount`` that is ``MEMBER_OF`` the organization, with an
|
|
18
18
|
``ACTS_AS`` edge from the agent. It is made with the agent and deleted
|
|
19
|
-
with it.
|
|
19
|
+
with it. Its slug is the slugified agent name, and its membership has
|
|
20
|
+
the ``developer`` role.
|
|
20
21
|
|
|
21
22
|
Every route needs the caller to be a member of the organization
|
|
22
23
|
(``MEMBER_OF``), because the ``agent:*`` permissions are not scoped to
|
|
@@ -32,6 +33,7 @@ import fastapi
|
|
|
32
33
|
import nanoid
|
|
33
34
|
import psycopg.errors
|
|
34
35
|
import pydantic
|
|
36
|
+
import slugify
|
|
35
37
|
|
|
36
38
|
from imbi.api import agent_tools, harness, workflows
|
|
37
39
|
from imbi.api.auth import organizations, permissions
|
|
@@ -421,13 +423,22 @@ CREATE (s:ServiceAccount {{id: {sa_id}, slug: {sa_slug},
|
|
|
421
423
|
display_name: {sa_display_name},
|
|
422
424
|
description: {sa_description},
|
|
423
425
|
is_active: true, created_at: {sa_created_at}}})
|
|
424
|
-
CREATE (s)-[:MEMBER_OF]->(o)
|
|
426
|
+
CREATE (s)-[:MEMBER_OF {{role: 'developer'}}]->(o)
|
|
425
427
|
CREATE (a)-[:ACTS_AS]->(s)
|
|
426
428
|
"""
|
|
427
429
|
|
|
430
|
+
_SA_SLUG_TAKEN_QUERY: typing.LiteralString = """
|
|
431
|
+
MATCH (s:ServiceAccount {{slug: {slug}}})
|
|
432
|
+
RETURN s.id AS id
|
|
433
|
+
"""
|
|
434
|
+
|
|
435
|
+
# A rename during concurrent lazy creation can give an agent two
|
|
436
|
+
# accounts. The order makes all later calls use the same one.
|
|
428
437
|
_SERVICE_ACCOUNT_QUERY: typing.LiteralString = """
|
|
429
438
|
MATCH (:Agent {{id: {id}}})-[:ACTS_AS]->(s:ServiceAccount)
|
|
430
439
|
RETURN s.id AS id
|
|
440
|
+
ORDER BY s.created_at, s.id
|
|
441
|
+
LIMIT 1
|
|
431
442
|
"""
|
|
432
443
|
|
|
433
444
|
_ENSURE_SERVICE_ACCOUNT_QUERY: typing.LiteralString = (
|
|
@@ -456,16 +467,32 @@ def _props(raw: typing.Any) -> dict[str, typing.Any] | None:
|
|
|
456
467
|
return typing.cast('dict[str, typing.Any]', parsed)
|
|
457
468
|
|
|
458
469
|
|
|
459
|
-
def _service_account_params(
|
|
470
|
+
async def _service_account_params(
|
|
471
|
+
db: graph.Graph, agent_id: str, name: str
|
|
472
|
+
) -> dict[str, typing.Any]:
|
|
460
473
|
"""Return the parameters of :data:`_CREATE_SERVICE_ACCOUNT`.
|
|
461
474
|
|
|
462
|
-
The slug
|
|
463
|
-
|
|
464
|
-
slug
|
|
475
|
+
The slug is the slugified agent name. Service account slugs are
|
|
476
|
+
unique across all organizations, so when another service account
|
|
477
|
+
has that slug, the agent id is added to it. The agent id is also
|
|
478
|
+
added when the name gives an empty slug, or a slug in the ``imbi-``
|
|
479
|
+
namespace of the internal service accounts. Seeding adopts an
|
|
480
|
+
existing account with an internal slug, so an agent must not have
|
|
481
|
+
one: a slug that still starts with ``imbi-`` gets an ``agent-``
|
|
482
|
+
prefix.
|
|
465
483
|
"""
|
|
484
|
+
slug = slugify.slugify(name)
|
|
485
|
+
if (
|
|
486
|
+
not slug
|
|
487
|
+
or slug.startswith('imbi-')
|
|
488
|
+
or await db.execute(_SA_SLUG_TAKEN_QUERY, {'slug': slug}, ['id'])
|
|
489
|
+
):
|
|
490
|
+
slug = slugify.slugify(f'{name} {agent_id}')
|
|
491
|
+
if slug.startswith('imbi-'):
|
|
492
|
+
slug = slugify.slugify(f'agent {slug}')
|
|
466
493
|
return {
|
|
467
494
|
'sa_id': nanoid.generate(),
|
|
468
|
-
'sa_slug':
|
|
495
|
+
'sa_slug': slug,
|
|
469
496
|
'sa_display_name': name,
|
|
470
497
|
'sa_description': f'Service principal of the agent {name!r}',
|
|
471
498
|
'sa_created_at': _now(),
|
|
@@ -483,18 +510,35 @@ async def ensure_service_account(
|
|
|
483
510
|
records = await db.execute(
|
|
484
511
|
_SERVICE_ACCOUNT_QUERY, {'id': agent_id}, ['id']
|
|
485
512
|
)
|
|
486
|
-
|
|
513
|
+
attempts = 3
|
|
514
|
+
while not records and attempts:
|
|
515
|
+
attempts -= 1
|
|
516
|
+
params = await _service_account_params(db, agent_id, name)
|
|
517
|
+
# Read again after the slug check. When a concurrent request
|
|
518
|
+
# made the account of this agent first, its slug looks taken,
|
|
519
|
+
# and this read finds that account. Thus the agent does not
|
|
520
|
+
# get a second account.
|
|
521
|
+
records = await db.execute(
|
|
522
|
+
_SERVICE_ACCOUNT_QUERY, {'id': agent_id}, ['id']
|
|
523
|
+
)
|
|
524
|
+
if records:
|
|
525
|
+
break
|
|
487
526
|
try:
|
|
527
|
+
# Empty when the agent does not exist.
|
|
488
528
|
records = await db.execute(
|
|
489
529
|
_ENSURE_SERVICE_ACCOUNT_QUERY,
|
|
490
|
-
{'id': agent_id, **
|
|
530
|
+
{'id': agent_id, **params},
|
|
491
531
|
['id'],
|
|
492
532
|
)
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
533
|
+
break
|
|
534
|
+
except psycopg.errors.UniqueViolation as exc:
|
|
535
|
+
if not attempts:
|
|
536
|
+
raise fastapi.HTTPException(
|
|
537
|
+
status_code=409,
|
|
538
|
+
detail=f'Service account slug for {name!r} is in use',
|
|
539
|
+
) from exc
|
|
540
|
+
# A concurrent request took the slug first. Try again.
|
|
541
|
+
continue
|
|
498
542
|
if not records:
|
|
499
543
|
raise fastapi.HTTPException(
|
|
500
544
|
status_code=404, detail=f'Agent {name!r} not found'
|
|
@@ -1160,17 +1204,38 @@ async def create_agent(
|
|
|
1160
1204
|
+ _CREATE_SERVICE_ACCOUNT
|
|
1161
1205
|
+ ' RETURN a.id AS id'
|
|
1162
1206
|
)
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1207
|
+
records: list[dict[str, typing.Any]] = []
|
|
1208
|
+
for attempt in range(2):
|
|
1209
|
+
try:
|
|
1210
|
+
records = await db.execute(
|
|
1211
|
+
query,
|
|
1212
|
+
{
|
|
1213
|
+
**props,
|
|
1214
|
+
**await _service_account_params(
|
|
1215
|
+
db, agent_id, snapshot.name
|
|
1216
|
+
),
|
|
1217
|
+
'org_slug': org_slug,
|
|
1218
|
+
'team_id': team_id,
|
|
1219
|
+
},
|
|
1220
|
+
['id'],
|
|
1221
|
+
)
|
|
1222
|
+
break
|
|
1223
|
+
except psycopg.errors.UniqueViolation as exc:
|
|
1224
|
+
# A concurrent create in another org can take the service
|
|
1225
|
+
# account slug first. The next attempt sees that slug as
|
|
1226
|
+
# taken and adds the agent id to it.
|
|
1227
|
+
if exc.diag.table_name != 'ServiceAccount':
|
|
1228
|
+
raise fastapi.HTTPException(
|
|
1229
|
+
status_code=409,
|
|
1230
|
+
detail=_slug_taken_detail(snapshot.slug),
|
|
1231
|
+
) from exc
|
|
1232
|
+
if attempt:
|
|
1233
|
+
raise fastapi.HTTPException(
|
|
1234
|
+
status_code=409,
|
|
1235
|
+
detail=(
|
|
1236
|
+
f'Service account slug for {snapshot.name!r} is in use'
|
|
1237
|
+
),
|
|
1238
|
+
) from exc
|
|
1174
1239
|
if not records:
|
|
1175
1240
|
raise fastapi.HTTPException(
|
|
1176
1241
|
status_code=404,
|
|
@@ -56,7 +56,6 @@ from imbi.api.secret_broker import store as secret_store
|
|
|
56
56
|
from imbi.common import graph, models
|
|
57
57
|
from imbi.common.prompts import rendering
|
|
58
58
|
from imbi.common.prompts import resolve as prompt_resolve
|
|
59
|
-
from imbi.plugins.github import _repos
|
|
60
59
|
|
|
61
60
|
LOGGER = logging.getLogger(__name__)
|
|
62
61
|
|
|
@@ -688,7 +687,7 @@ async def _rendered_prompt(
|
|
|
688
687
|
"""Render the pinned prompt version; return it or the error.
|
|
689
688
|
|
|
690
689
|
The template gets the variables that it declares among
|
|
691
|
-
``organization``, ``agent``, ``task``, and ``
|
|
690
|
+
``organization``, ``agent``, ``task``, and ``project_slug`` (slugs and
|
|
692
691
|
the short id).
|
|
693
692
|
"""
|
|
694
693
|
if bundle.prompt_ref is None or bundle.prompt_version is None:
|
|
@@ -705,7 +704,7 @@ async def _rendered_prompt(
|
|
|
705
704
|
'organization': org_slug,
|
|
706
705
|
'agent': bundle.agent_slug or '',
|
|
707
706
|
'task': task['short_id'],
|
|
708
|
-
'
|
|
707
|
+
'project_slug': task.get('project_slug') or '',
|
|
709
708
|
}
|
|
710
709
|
rendered = await rendering.render(
|
|
711
710
|
version,
|
|
@@ -746,23 +745,27 @@ async def _repositories(
|
|
|
746
745
|
the GitHub integration verified, whatever this link says."""
|
|
747
746
|
if not task.get('project_id'):
|
|
748
747
|
return []
|
|
749
|
-
|
|
748
|
+
config: dict[str, typing.Any] = {}
|
|
750
749
|
if settings.github_app_binding is not None:
|
|
751
750
|
provider = await store.binding_provider(
|
|
752
751
|
task['organization_id'], settings.github_app_binding
|
|
753
752
|
)
|
|
754
753
|
if provider is not None and provider['type'] == 'github_app':
|
|
755
|
-
config
|
|
756
|
-
host = str(config.get('host', host))
|
|
754
|
+
config = provider['config'] or {}
|
|
757
755
|
url = (await _helpers.lookup_project_links(db, task['project_id'])).get(
|
|
758
756
|
_REPOSITORY_LINK
|
|
759
757
|
)
|
|
760
|
-
|
|
761
|
-
if url is None or found is None:
|
|
758
|
+
if url is None:
|
|
762
759
|
return []
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
760
|
+
try:
|
|
761
|
+
# The provider parses the link: an empty configuration is
|
|
762
|
+
# github.com.
|
|
763
|
+
found = providers.get('github_app').repository(config, url)
|
|
764
|
+
except providers.BrokerError:
|
|
765
|
+
return []
|
|
766
|
+
if found is None:
|
|
767
|
+
return []
|
|
768
|
+
return [BundleRepository(host=found[0], name=found[1], url=url)]
|
|
766
769
|
|
|
767
770
|
|
|
768
771
|
async def _identity(
|
|
@@ -1806,35 +1809,40 @@ async def create_harness_child_task(
|
|
|
1806
1809
|
'short_id': parent['short_id'],
|
|
1807
1810
|
'agent_id': parent['agent_id'],
|
|
1808
1811
|
}
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1812
|
+
try:
|
|
1813
|
+
task, created = await store.create(
|
|
1814
|
+
agent_tasks.NewTask(
|
|
1815
|
+
organization_id=parent['organization_id'],
|
|
1816
|
+
agent_id=agent_id,
|
|
1817
|
+
agent_version=agent_version,
|
|
1818
|
+
prompt_version=prompt_version,
|
|
1819
|
+
service_account_id=service_account_id,
|
|
1820
|
+
project_id=parent['project_id'],
|
|
1821
|
+
project_slug=parent['project_slug'],
|
|
1822
|
+
title=data.title,
|
|
1823
|
+
description=data.description,
|
|
1824
|
+
origin_kind='task',
|
|
1825
|
+
origin_id=origin_id,
|
|
1826
|
+
origin=origin,
|
|
1827
|
+
idempotency_key=data.idempotency_key,
|
|
1828
|
+
owner=parent['owner'],
|
|
1829
|
+
budget=budget,
|
|
1830
|
+
chain_id=chain_id,
|
|
1831
|
+
),
|
|
1832
|
+
agent_tasks.Actor('agent', parent['agent_id'], 'harness'),
|
|
1833
|
+
{
|
|
1834
|
+
'title': data.title,
|
|
1835
|
+
'description': data.description,
|
|
1836
|
+
'origin': origin,
|
|
1837
|
+
'budget': None if budget is None else str(budget),
|
|
1838
|
+
'agent_version': agent_version,
|
|
1839
|
+
'prompt_version': prompt_version,
|
|
1840
|
+
},
|
|
1841
|
+
)
|
|
1842
|
+
except agent_tasks.TaskClosed as e:
|
|
1843
|
+
raise _conflict(
|
|
1844
|
+
'task_closed', f'Task {parent["short_id"]} is closed'
|
|
1845
|
+
) from e
|
|
1838
1846
|
if not created:
|
|
1839
1847
|
response.status_code = 200
|
|
1840
1848
|
return task
|