devops-bot-sdk 1.6.64__tar.gz → 1.6.66__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.
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/PKG-INFO +1 -1
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/__init__.py +2 -2
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/activity/models.py +52 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/activity/redact.py +4 -1
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/activity/shipper.py +28 -8
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/activity/spool.py +19 -5
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_activity_core.py +1 -1
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_activity_pipeline.py +61 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/README.md +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/devops_bot_sdk.egg-info/SOURCES.txt +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/devops_bot_sdk.egg-info/requires.txt +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/devops_bot_sdk.egg-info/top_level.txt +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/pyproject.toml +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/activity/__init__.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/activity/classifier.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/activity/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/activity/collectors/common.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/activity/collectors/linux_auditd.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/activity/collectors/macos_eslogger.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/activity/collectors/windows_eventlog.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/activity/os_setup.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/activity/recorder.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/activity/settings.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/agentd.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/agents/__init__.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/agents/activity.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/agents/builder.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/agents/chat.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/agents/connectors.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/agents/engines/__init__.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/agents/engines/claude_code.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/agents/engines/http.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/agents/engines/mcp.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/agents/engines/n8n.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/agents/engines/shell.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/agents/integrations.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/agents/inventory.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/agents/library/__init__.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/agents/manifest.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/agents/prompt_studio.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/agents/prompt_template.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/agents/prompt_vars.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/agents/run_log.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/agents/schedules.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/agents/secrets.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/agents/ticket_run.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/agents/usage.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/agents/varstore.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/browser_profile.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/cli.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/client.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/collectors/files.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/collectors/process.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/collectors/screenshot.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/config.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/crucial.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/custom_agents.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/departments.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/design_verify/__init__.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/design_verify/assets.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/design_verify/bootstrap.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/design_verify/browser.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/design_verify/compare.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/design_verify/loop.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/estimator.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/exceptions.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/firewall.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/formats.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/git_ops.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/github_fetch.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/github_ingest.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/graph_recon.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/graphify.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/hooks/__init__.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/hooks/crucial_guard.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/intake.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ipc/__init__.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ipc/electron_bridge.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ipc/handlers.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/journal.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/local_exec.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/memory_guard.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/models/__init__.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/models/envelope.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/models/requests.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/models/responses.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/models/snapshots.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/phase_timer.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/plan_run.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/platform_compat.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/prd_progress.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/proof_readers.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/providers.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/py.typed +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/resume.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/router.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/run_auto.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/scheduler.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/self_report.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/self_report_local.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/sse.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/swagger.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/task_file.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/task_types.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/test.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/test_pipeline.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ticket_fields.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/tickets.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/tiers/__init__.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/tiers/mini_agent.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ui/__init__.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ui/api.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ui/dashboard.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ui/health.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ui/ip_allowlist.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ui/jobs.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ui/models.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ui/runner.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ui/security.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ui/server.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ui/service.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ui/session.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ui/setup_manager.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ui/static/agents.html +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ui/static/app.js +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ui/static/chat.html +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ui/static/custom-agents.html +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ui/static/default-agents.html +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ui/static/index.html +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ui/static/report.html +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ui/static/runs.html +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ui/static/schedules.html +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ui/static/setup.html +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ui/static/styles.css +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ui/static/tickets.html +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/ui/swagger.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/updater.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/url_access.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/vendor/aidraftsman-cli-0.1.0.tgz +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/vps_health.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/sdk/write_hook.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/setup.cfg +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_activity_collectors.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_activity_macos.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_activity_os_setup.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_agent_activity.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_agent_activity_tracking.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_agent_chat_features.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_agent_crud_cli.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_agent_listing.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_agent_prompt_async.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_agent_prompt_models.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_agent_run_ticket.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_agent_schedules.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_agent_workspace_settings.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_agents_connectors_builder.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_allowed_tools_config.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_auto_agent_dispatch.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_browser_profile.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_cheap_tier.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_client_jira_projects.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_config_token_diagnostics.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_configure_token_owner.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_console_encoding.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_crash_recovery.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_departments.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_done_transition.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_epic_decompose_run.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_epic_inherited_agent_id.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_epic_session_isolation.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_estimator.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_fallback_cli.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_firewall_open_on_update.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_formats.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_git_action.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_github_fetch.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_github_ingest.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_graph_recon.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_graphify_build.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_graphify_hook_install.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_health.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_intake_job_outcome.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_intake_plan_review.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_journal.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_local_exec_provider.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_mcp_route_contract.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_memory_guard.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_phase_timer.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_plan_new_command.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_plan_run_activity.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_pr_identity.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_prd_artifact.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_project_path_resolution.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_proof_readers.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_providers.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_recon_session_isolation.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_resume.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_router.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_run_auto_agent_only.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_run_outputs.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_run_verdict_is_honest.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_schedule_api.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_schedule_outputs_and_plans.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_scheduler.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_self_report.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_service_token_warning.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_stale_panel_restart.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_swagger_command.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_task_file_and_plan_run.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_task_file_format_doc.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_ticket_cli.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_ticket_fields.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_ticket_run_resolve_by_id.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_ticket_speed_fixes.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_tier_flag_parity.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_ui_gate.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_ui_lifespan.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_update_timer.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_updater_extras.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_updater_pep668.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_updater_reports_the_truth.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_url_access.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_vps_health.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_windows_loop_identity.py +0 -0
- {devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/tests/test_write_hook.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""AgentOS Desktop SDK — thin HTTPS/SSE client for the Electron app.
|
|
2
2
|
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.66
|
|
4
4
|
|
|
5
5
|
Public surface:
|
|
6
6
|
BackendClient.from_config() — create client from ~/.agentos/config.toml
|
|
@@ -34,7 +34,7 @@ Rules:
|
|
|
34
34
|
- All data egress through submit_webhook only
|
|
35
35
|
"""
|
|
36
36
|
|
|
37
|
-
__version__ = "1.6.
|
|
37
|
+
__version__ = "1.6.66" # SINGLE SOURCE OF TRUTH — bump on every change; pyproject,
|
|
38
38
|
__author__ = "AgentOS" # sdk.client.SDK_VERSION and the UI server version all read this.
|
|
39
39
|
|
|
40
40
|
from sdk.client import BackendClient
|
|
@@ -16,6 +16,7 @@ retried at any hop is stored once.
|
|
|
16
16
|
"""
|
|
17
17
|
from __future__ import annotations
|
|
18
18
|
|
|
19
|
+
import json
|
|
19
20
|
import os
|
|
20
21
|
import sys
|
|
21
22
|
import time
|
|
@@ -140,6 +141,57 @@ def _wire(obj: Any) -> dict[str, Any]:
|
|
|
140
141
|
return out
|
|
141
142
|
|
|
142
143
|
|
|
144
|
+
#: String caps the relay validates (devops-bot-ai ``modules/vps_activity/schemas.py``).
|
|
145
|
+
#: A value over its cap is a 422, and the shipper drops a row the relay refuses — so
|
|
146
|
+
#: one over-long path or command line used to cost the whole event. Clamp instead.
|
|
147
|
+
WIRE_MAX_CHARS: dict[str, dict[str, int]] = {
|
|
148
|
+
"event": {
|
|
149
|
+
"action": 100,
|
|
150
|
+
"osUser": 255,
|
|
151
|
+
"processName": 255,
|
|
152
|
+
"command": 20_000,
|
|
153
|
+
"target": 4096,
|
|
154
|
+
"agentRunId": 128,
|
|
155
|
+
"sessionKey": 128,
|
|
156
|
+
},
|
|
157
|
+
"session": {
|
|
158
|
+
"osUser": 255,
|
|
159
|
+
"remoteIp": 45,
|
|
160
|
+
"agentRunId": 128,
|
|
161
|
+
"initiatedByKey": 128,
|
|
162
|
+
},
|
|
163
|
+
}
|
|
164
|
+
_CLAMP_MARK = "…"
|
|
165
|
+
#: Free-form dicts the relay does not cap. Bounded here so that, with the string caps
|
|
166
|
+
#: above, no single row can outgrow a request (2 MB gzipped / 10 MB raw) — a row that
|
|
167
|
+
#: could would be 413'd on its own and dropped.
|
|
168
|
+
WIRE_DICT_FIELDS = ("metadata", "actorReasons")
|
|
169
|
+
MAX_DICT_FIELD_BYTES = 64 * 1024
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
def fit_wire_limits(kind: str, wire: dict[str, Any]) -> dict[str, Any]:
|
|
173
|
+
"""``wire`` with every over-long field shortened to fit the relay's limits."""
|
|
174
|
+
out = wire
|
|
175
|
+
|
|
176
|
+
def _set(name: str, value: Any) -> None:
|
|
177
|
+
nonlocal out
|
|
178
|
+
if out is wire:
|
|
179
|
+
out = dict(wire)
|
|
180
|
+
out[name] = value
|
|
181
|
+
|
|
182
|
+
for name, cap in WIRE_MAX_CHARS.get(kind, {}).items():
|
|
183
|
+
value = wire.get(name)
|
|
184
|
+
if isinstance(value, str) and len(value) > cap:
|
|
185
|
+
_set(name, value[: cap - len(_CLAMP_MARK)] + _CLAMP_MARK)
|
|
186
|
+
for name in WIRE_DICT_FIELDS:
|
|
187
|
+
value = wire.get(name)
|
|
188
|
+
if isinstance(value, dict):
|
|
189
|
+
size = len(json.dumps(value, separators=(",", ":"), default=str))
|
|
190
|
+
if size > MAX_DICT_FIELD_BYTES:
|
|
191
|
+
_set(name, {"truncated": True, "originalBytes": size})
|
|
192
|
+
return out
|
|
193
|
+
|
|
194
|
+
|
|
143
195
|
# ── models ───────────────────────────────────────────────────────────────────
|
|
144
196
|
|
|
145
197
|
|
|
@@ -72,6 +72,7 @@ _PATTERNS: list[tuple[re.Pattern[str], str]] = [
|
|
|
72
72
|
|
|
73
73
|
#: Hard cap so a pathological command line can't bloat the spool.
|
|
74
74
|
MAX_COMMAND_CHARS = 20_000
|
|
75
|
+
TRUNCATED = "…[truncated]"
|
|
75
76
|
|
|
76
77
|
|
|
77
78
|
def redact_command(command: str | None) -> str | None:
|
|
@@ -82,5 +83,7 @@ def redact_command(command: str | None) -> str | None:
|
|
|
82
83
|
for pattern, repl in _PATTERNS:
|
|
83
84
|
out = pattern.sub(repl, out)
|
|
84
85
|
if len(out) > MAX_COMMAND_CHARS:
|
|
85
|
-
|
|
86
|
+
# The marker counts toward the cap: the relay 422s anything longer, and the
|
|
87
|
+
# shipper drops a refused event rather than shipping it truncated.
|
|
88
|
+
out = out[: MAX_COMMAND_CHARS - len(TRUNCATED)] + TRUNCATED
|
|
86
89
|
return out
|
|
@@ -7,8 +7,9 @@ Delivery rules — a row leaves the spool ONLY when the relay answered 202 (or i
|
|
|
7
7
|
provably undeliverable):
|
|
8
8
|
|
|
9
9
|
2xx delivered → delete the chunk
|
|
10
|
-
network error, 5xx, 408
|
|
10
|
+
network error, 5xx, 408 retry (short backoff); if still failing, keep
|
|
11
11
|
everything and try again at the next flush
|
|
12
|
+
429 wait out the relay's 60 s window, then retry
|
|
12
13
|
401, 426 token / SDK version needs an operator → keep, stop
|
|
13
14
|
this flush (retrying in a tight loop helps nobody)
|
|
14
15
|
any other 4xx the relay READ the chunk and refused it. Dropping all
|
|
@@ -16,8 +17,9 @@ provably undeliverable):
|
|
|
16
17
|
so the chunk is BISECTED until the offending row is
|
|
17
18
|
isolated; only that single row is dropped.
|
|
18
19
|
|
|
19
|
-
A flush drains the whole spool, chunk by chunk (≤500 events / ≤
|
|
20
|
-
five minutes of full OS auditing is
|
|
20
|
+
A flush drains the whole spool, chunk by chunk (≤500 events / ≤4 MB each), paced under
|
|
21
|
+
the relay's 60-requests-a-minute budget, because five minutes of full OS auditing is
|
|
22
|
+
routinely thousands of events.
|
|
21
23
|
"""
|
|
22
24
|
from __future__ import annotations
|
|
23
25
|
|
|
@@ -36,9 +38,18 @@ logger = logging.getLogger("sdk.activity.shipper")
|
|
|
36
38
|
RETRY_DELAYS_S = (2.0, 8.0)
|
|
37
39
|
#: Safety cap on chunks per flush so one flush can't run away.
|
|
38
40
|
MAX_CHUNKS_PER_FLUSH = 200
|
|
41
|
+
#: The relay allows 60 batches per minute per token. Firing chunks back-to-back blew
|
|
42
|
+
#: through that after ~60 chunks; the 2 s / 8 s retries cannot outlast a 60 s window,
|
|
43
|
+
#: so the flush gave up and a busy host never caught up. Pace below the budget...
|
|
44
|
+
MIN_POST_GAP_S = 1.1
|
|
45
|
+
#: ...and if a 429 still lands (another process on the same token), wait the window
|
|
46
|
+
#: out rather than abandoning the flush.
|
|
47
|
+
RATE_LIMIT_WAIT_S = 61.0
|
|
48
|
+
MAX_RATE_LIMIT_WAITS = 3
|
|
39
49
|
|
|
40
50
|
_NEEDS_OPERATOR = {401, 426}
|
|
41
51
|
_TRANSIENT_4XX = {408, 429}
|
|
52
|
+
_RATE_LIMITED = 429
|
|
42
53
|
|
|
43
54
|
Sleep = Callable[[float], Awaitable[Any]]
|
|
44
55
|
|
|
@@ -73,20 +84,27 @@ def _verdict(status: int | None) -> str:
|
|
|
73
84
|
|
|
74
85
|
async def _attempt(sender: Sender, chunk: Chunk, sleep: Sleep) -> str:
|
|
75
86
|
"""One chunk with short in-flush retries → ``ok`` / ``drop`` / ``stop`` / ``later``."""
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
87
|
+
retry_delays = list(RETRY_DELAYS_S)
|
|
88
|
+
rate_limit_waits = 0
|
|
89
|
+
while True:
|
|
79
90
|
try:
|
|
80
91
|
status: int | None = await sender.post_vps_activity_batch(chunk.to_wire())
|
|
81
92
|
except Exception as exc: # noqa: BLE001 — httpx errors, DNS, TLS, ...
|
|
82
93
|
logger.warning("activity: ship failed (%s); will retry", exc)
|
|
83
94
|
status = None
|
|
95
|
+
if status == _RATE_LIMITED and rate_limit_waits < MAX_RATE_LIMIT_WAITS:
|
|
96
|
+
rate_limit_waits += 1
|
|
97
|
+
logger.info("activity: relay rate-limited; waiting %.0fs", RATE_LIMIT_WAIT_S)
|
|
98
|
+
await sleep(RATE_LIMIT_WAIT_S)
|
|
99
|
+
continue
|
|
84
100
|
verdict = _verdict(status)
|
|
85
101
|
if verdict != "retry":
|
|
86
102
|
if verdict == "stop":
|
|
87
103
|
logger.warning("activity: relay answered %s — needs operator attention", status)
|
|
88
104
|
return verdict
|
|
89
|
-
|
|
105
|
+
if not retry_delays:
|
|
106
|
+
return "later"
|
|
107
|
+
await sleep(retry_delays.pop(0))
|
|
90
108
|
|
|
91
109
|
|
|
92
110
|
async def _deliver(
|
|
@@ -135,10 +153,12 @@ async def flush_once(
|
|
|
135
153
|
from sdk.client import BackendClient
|
|
136
154
|
|
|
137
155
|
sender = BackendClient.from_config()
|
|
138
|
-
for
|
|
156
|
+
for i in range(max_chunks):
|
|
139
157
|
chunk = spool.next_chunk()
|
|
140
158
|
if not chunk:
|
|
141
159
|
break
|
|
160
|
+
if i:
|
|
161
|
+
await sleep(MIN_POST_GAP_S)
|
|
142
162
|
if not await _deliver(spool, sender, chunk, result, sleep):
|
|
143
163
|
break
|
|
144
164
|
else:
|
|
@@ -23,14 +23,21 @@ from dataclasses import dataclass, field
|
|
|
23
23
|
from pathlib import Path
|
|
24
24
|
from typing import Any
|
|
25
25
|
|
|
26
|
+
from sdk.activity.models import fit_wire_limits
|
|
27
|
+
|
|
26
28
|
logger = logging.getLogger("sdk.activity.spool")
|
|
27
29
|
|
|
28
30
|
#: Rows kept at most. ~200k events is days of a busy host; beyond it, shed oldest.
|
|
29
31
|
DEFAULT_MAX_ROWS = 200_000
|
|
30
|
-
#: Wire limits per request — the relay and the gateway enforce the same cap.
|
|
32
|
+
#: Wire limits per request — the relay and the gateway enforce the same row cap.
|
|
31
33
|
MAX_EVENTS_PER_CHUNK = 500
|
|
32
34
|
MAX_SESSIONS_PER_CHUNK = 500
|
|
33
|
-
|
|
35
|
+
#: Uncompressed JSON per request. The relay takes 10 MB decompressed / 2 MB gzipped;
|
|
36
|
+
#: audit JSON gzips well past 2:1, so 4 MB stays under both. A 1 MB cap meant a host
|
|
37
|
+
#: with long command lines sent small chunks, ran into the relay's per-minute request
|
|
38
|
+
#: budget and fell behind until the spool shed its oldest rows. A chunk that still
|
|
39
|
+
#: comes back 413 is bisected by the shipper, so this is a target, not a cliff.
|
|
40
|
+
MAX_CHUNK_BYTES = 4_000_000
|
|
34
41
|
|
|
35
42
|
_SCHEMA = """
|
|
36
43
|
CREATE TABLE IF NOT EXISTS spool (
|
|
@@ -110,7 +117,10 @@ class Spool:
|
|
|
110
117
|
with self._lock:
|
|
111
118
|
self._db.execute(
|
|
112
119
|
"INSERT OR IGNORE INTO spool (kind, key, payload) VALUES ('event', ?, ?)",
|
|
113
|
-
(
|
|
120
|
+
(
|
|
121
|
+
wire["eventId"],
|
|
122
|
+
json.dumps(fit_wire_limits("event", wire), separators=(",", ":")),
|
|
123
|
+
),
|
|
114
124
|
)
|
|
115
125
|
self._trim()
|
|
116
126
|
|
|
@@ -127,7 +137,10 @@ class Spool:
|
|
|
127
137
|
"INSERT INTO spool (kind, key, payload) VALUES ('session', ?, ?) "
|
|
128
138
|
"ON CONFLICT (kind, key) DO UPDATE SET "
|
|
129
139
|
"payload = excluded.payload, rev = rev + 1",
|
|
130
|
-
(
|
|
140
|
+
(
|
|
141
|
+
wire["externalKey"],
|
|
142
|
+
json.dumps(fit_wire_limits("session", wire), separators=(",", ":")),
|
|
143
|
+
),
|
|
131
144
|
)
|
|
132
145
|
self._trim()
|
|
133
146
|
|
|
@@ -176,7 +189,8 @@ class Spool:
|
|
|
176
189
|
break
|
|
177
190
|
if chunk.rows and size + len(payload) > max_bytes:
|
|
178
191
|
break
|
|
179
|
-
|
|
192
|
+
# Re-fit on the way out too: rows queued by an older SDK predate the clamp.
|
|
193
|
+
chunk.rows.append(Row(seq, rev, kind, fit_wire_limits(kind, json.loads(payload))))
|
|
180
194
|
n_events += kind == "event"
|
|
181
195
|
n_sessions += kind == "session"
|
|
182
196
|
size += len(payload)
|
|
@@ -176,7 +176,7 @@ class TestRedaction:
|
|
|
176
176
|
def test_is_length_capped(self):
|
|
177
177
|
out = redact_command("a" * (MAX_COMMAND_CHARS + 500))
|
|
178
178
|
assert out.endswith("…[truncated]")
|
|
179
|
-
assert len(out)
|
|
179
|
+
assert len(out) == MAX_COMMAND_CHARS # the relay 422s anything longer
|
|
180
180
|
|
|
181
181
|
|
|
182
182
|
class TestClassifier:
|
|
@@ -66,6 +66,45 @@ class TestSpool:
|
|
|
66
66
|
_sess("S1", actorType="HUMAN", endedAt="2026-10-09T12:00:00.000Z")
|
|
67
67
|
]
|
|
68
68
|
|
|
69
|
+
def test_over_long_fields_are_clamped_to_the_relay_caps_not_left_to_be_refused(self, spool):
|
|
70
|
+
from sdk.activity.models import WIRE_MAX_CHARS
|
|
71
|
+
|
|
72
|
+
spool.add_event(_ev("E1", command="c" * 30_000, target="/t" * 5000, processName="p" * 999))
|
|
73
|
+
spool.add_session(_sess("S1", osUser="u" * 999))
|
|
74
|
+
chunk = spool.next_chunk()
|
|
75
|
+
ev, sess = chunk.events[0], chunk.sessions[0]
|
|
76
|
+
for name in ("command", "target", "processName"):
|
|
77
|
+
assert len(ev[name]) == WIRE_MAX_CHARS["event"][name]
|
|
78
|
+
assert len(sess["osUser"]) == WIRE_MAX_CHARS["session"]["osUser"]
|
|
79
|
+
assert ev["action"] == "exec" # short fields untouched
|
|
80
|
+
|
|
81
|
+
def test_an_oversized_metadata_dict_is_replaced_so_the_row_still_ships(self, spool):
|
|
82
|
+
from sdk.activity.models import MAX_DICT_FIELD_BYTES
|
|
83
|
+
|
|
84
|
+
spool.add_event(_ev("E1", metadata={"blob": "x" * (MAX_DICT_FIELD_BYTES * 2)}))
|
|
85
|
+
spool.add_event(_ev("E2", metadata={"small": 1}))
|
|
86
|
+
events = spool.next_chunk().events
|
|
87
|
+
assert events[0]["metadata"]["truncated"] is True
|
|
88
|
+
assert events[0]["metadata"]["originalBytes"] > MAX_DICT_FIELD_BYTES
|
|
89
|
+
assert events[1]["metadata"] == {"small": 1}
|
|
90
|
+
|
|
91
|
+
def test_a_single_worst_case_row_fits_comfortably_in_one_request(self, spool):
|
|
92
|
+
from sdk.activity.models import MAX_DICT_FIELD_BYTES
|
|
93
|
+
|
|
94
|
+
spool.add_event(_ev(
|
|
95
|
+
"E1", command="c" * 99_999, target="t" * 99_999, processName="p" * 9_999,
|
|
96
|
+
metadata={"blob": "x" * (MAX_DICT_FIELD_BYTES * 10)},
|
|
97
|
+
))
|
|
98
|
+
(row,) = spool.next_chunk().rows
|
|
99
|
+
import json as _json
|
|
100
|
+
|
|
101
|
+
assert len(_json.dumps(row.wire)) < 200_000 # vs 2 MB gzipped / 10 MB raw at the relay
|
|
102
|
+
|
|
103
|
+
def test_default_chunk_byte_cap_stays_under_the_relay_body_limit(self):
|
|
104
|
+
from sdk.activity import spool as spool_mod
|
|
105
|
+
|
|
106
|
+
assert 1_000_000 < spool_mod.MAX_CHUNK_BYTES < 10 * 1024 * 1024
|
|
107
|
+
|
|
69
108
|
def test_rows_come_back_in_arrival_order_session_first(self, spool):
|
|
70
109
|
spool.add_session(_sess("S1"))
|
|
71
110
|
spool.add_event(_ev("E1"))
|
|
@@ -231,6 +270,28 @@ class TestShipper:
|
|
|
231
270
|
result = _run(shipper.flush_once(spool, sender, sleep=Sleeps()))
|
|
232
271
|
assert result.shipped_rows == 1
|
|
233
272
|
|
|
273
|
+
def test_a_429_waits_out_the_relay_window_instead_of_giving_up(self, spool):
|
|
274
|
+
spool.add_event(_ev("E1"))
|
|
275
|
+
sender = FakeSender(script=[429, 429, 429, 202])
|
|
276
|
+
sleeps = Sleeps()
|
|
277
|
+
result = _run(shipper.flush_once(spool, sender, sleep=sleeps))
|
|
278
|
+
assert result.shipped_rows == 1 and result.stopped is None
|
|
279
|
+
assert sleeps.delays == [shipper.RATE_LIMIT_WAIT_S] * 3
|
|
280
|
+
|
|
281
|
+
def test_endless_429s_keep_the_data_for_the_next_flush(self, spool):
|
|
282
|
+
spool.add_event(_ev("E1"))
|
|
283
|
+
sender = FakeSender(default=429)
|
|
284
|
+
result = _run(shipper.flush_once(spool, sender, sleep=Sleeps()))
|
|
285
|
+
assert result.stopped == "later" and spool.count() == 1
|
|
286
|
+
|
|
287
|
+
def test_chunks_are_paced_under_the_relay_request_budget(self, spool):
|
|
288
|
+
for i in range(1200):
|
|
289
|
+
spool.add_event(_ev(f"E{i:05d}"))
|
|
290
|
+
sleeps = Sleeps()
|
|
291
|
+
_run(shipper.flush_once(spool, FakeSender(), sleep=sleeps))
|
|
292
|
+
assert sleeps.delays == [shipper.MIN_POST_GAP_S] * 2 # 3 chunks, 2 gaps
|
|
293
|
+
assert shipper.MIN_POST_GAP_S * 60 > 60 # stays under 60 requests a minute
|
|
294
|
+
|
|
234
295
|
@pytest.mark.parametrize("status", [401, 426])
|
|
235
296
|
def test_token_or_version_problems_stop_without_retrying_or_dropping(self, spool, status):
|
|
236
297
|
spool.add_event(_ev("E1"))
|
|
File without changes
|
|
File without changes
|
{devops_bot_sdk-1.6.64 → devops_bot_sdk-1.6.66}/devops_bot_sdk.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|