devops-bot-sdk 1.6.44__tar.gz → 1.6.46__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.44 → devops_bot_sdk-1.6.46}/PKG-INFO +1 -1
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/__init__.py +2 -2
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/activity/collectors/windows_eventlog.py +28 -1
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/service.py +87 -3
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_activity_collectors.py +30 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_update_timer.py +94 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/README.md +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/devops_bot_sdk.egg-info/SOURCES.txt +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/devops_bot_sdk.egg-info/requires.txt +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/devops_bot_sdk.egg-info/top_level.txt +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/pyproject.toml +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/activity/__init__.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/activity/classifier.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/activity/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/activity/collectors/common.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/activity/collectors/linux_auditd.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/activity/collectors/macos_eslogger.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/activity/models.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/activity/os_setup.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/activity/recorder.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/activity/redact.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/activity/settings.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/activity/shipper.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/activity/spool.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agentd.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/__init__.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/activity.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/builder.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/chat.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/connectors.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/engines/__init__.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/engines/claude_code.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/engines/http.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/engines/mcp.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/engines/n8n.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/engines/shell.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/integrations.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/inventory.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/library/__init__.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/manifest.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/prompt_studio.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/prompt_template.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/prompt_vars.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/run_log.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/schedules.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/secrets.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/ticket_run.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/usage.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/agents/varstore.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/browser_profile.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/cli.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/client.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/collectors/files.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/collectors/process.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/collectors/screenshot.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/config.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/crucial.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/departments.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/design_verify/__init__.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/design_verify/assets.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/design_verify/bootstrap.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/design_verify/browser.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/design_verify/compare.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/design_verify/loop.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/estimator.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/exceptions.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/firewall.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/formats.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/git_ops.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/github_fetch.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/github_ingest.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/graph_recon.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/graphify.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/hooks/__init__.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/hooks/crucial_guard.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/intake.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ipc/__init__.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ipc/electron_bridge.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ipc/handlers.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/journal.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/local_exec.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/memory_guard.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/models/__init__.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/models/envelope.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/models/requests.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/models/responses.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/models/snapshots.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/phase_timer.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/plan_run.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/platform_compat.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/prd_progress.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/proof_readers.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/providers.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/py.typed +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/resume.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/router.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/run_auto.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/scheduler.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/self_report.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/self_report_local.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/sse.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/swagger.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/task_file.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/task_types.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/test.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/test_pipeline.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ticket_fields.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/tiers/__init__.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/tiers/mini_agent.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/__init__.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/api.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/dashboard.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/health.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/ip_allowlist.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/jobs.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/models.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/runner.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/security.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/server.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/session.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/setup_manager.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/static/agents.html +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/static/app.js +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/static/chat.html +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/static/custom-agents.html +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/static/default-agents.html +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/static/index.html +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/static/report.html +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/static/runs.html +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/static/schedules.html +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/static/setup.html +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/static/styles.css +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/static/tickets.html +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/ui/swagger.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/updater.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/url_access.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/vps_health.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/sdk/write_hook.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/setup.cfg +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_activity_core.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_activity_macos.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_activity_os_setup.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_activity_pipeline.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_agent_activity.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_agent_activity_tracking.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_agent_chat_features.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_agent_listing.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_agent_prompt_async.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_agent_prompt_models.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_agent_run_ticket.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_agent_schedules.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_agent_workspace_settings.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_agents_connectors_builder.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_allowed_tools_config.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_auto_agent_dispatch.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_browser_profile.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_cheap_tier.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_client_jira_projects.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_config_token_diagnostics.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_configure_token_owner.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_console_encoding.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_crash_recovery.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_departments.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_done_transition.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_epic_decompose_run.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_epic_inherited_agent_id.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_epic_session_isolation.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_estimator.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_firewall_open_on_update.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_formats.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_git_action.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_github_fetch.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_github_ingest.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_graph_recon.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_graphify_build.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_graphify_hook_install.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_health.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_intake_job_outcome.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_intake_plan_review.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_journal.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_local_exec_provider.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_mcp_route_contract.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_memory_guard.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_phase_timer.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_plan_new_command.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_plan_run_activity.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_pr_identity.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_prd_artifact.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_project_path_resolution.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_proof_readers.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_providers.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_recon_session_isolation.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_resume.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_router.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_run_auto_agent_only.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_run_outputs.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_run_verdict_is_honest.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_schedule_api.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_schedule_outputs_and_plans.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_scheduler.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_self_report.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_service_token_warning.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_swagger_command.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_task_file_and_plan_run.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_task_file_format_doc.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_ticket_fields.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_ticket_run_resolve_by_id.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_ticket_speed_fixes.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_tier_flag_parity.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_ui_gate.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_ui_lifespan.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_updater_extras.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_updater_reports_the_truth.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_url_access.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_vps_health.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/tests/test_windows_loop_identity.py +0 -0
- {devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/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.46
|
|
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.46" # 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
|
|
@@ -14,6 +14,11 @@ The event parser and the processor are pure; only :class:`WindowsEventLogCollect
|
|
|
14
14
|
touches the OS. Processes created by SYSTEM / LOCAL SERVICE / NETWORK SERVICE are
|
|
15
15
|
skipped by default (``ignore_system``): they are thousands of events an hour of
|
|
16
16
|
Windows housekeeping that no human or agent did.
|
|
17
|
+
|
|
18
|
+
The collector's own polling is skipped too: every poll runs ``wevtutil.exe`` from the
|
|
19
|
+
panel process (as the panel's user, not SYSTEM), and each of those is itself a 4688.
|
|
20
|
+
Unfiltered, that is ~8,600 "Administrator ran wevtutil" events a day per VPS, each
|
|
21
|
+
poll recording the previous one.
|
|
17
22
|
"""
|
|
18
23
|
from __future__ import annotations
|
|
19
24
|
|
|
@@ -47,6 +52,9 @@ logger = logging.getLogger("sdk.activity.collectors.windows")
|
|
|
47
52
|
_NS = "{http://schemas.microsoft.com/win/2004/08/events/event}"
|
|
48
53
|
EVENT_IDS = (4688, 4624, 4634, 4647, 4625)
|
|
49
54
|
_MAX_PID_CACHE = 20_000
|
|
55
|
+
#: Executables the collector itself spawns to poll the log.
|
|
56
|
+
_OWN_TOOLS = ("wevtutil.exe",)
|
|
57
|
+
_MAX_OWN_PIDS = 1_000
|
|
50
58
|
|
|
51
59
|
#: Logon types that are a PERSON at a keyboard / remote desktop.
|
|
52
60
|
_INTERACTIVE = {2: SessionKind.CONSOLE, 11: SessionKind.CONSOLE, 10: SessionKind.RDP}
|
|
@@ -123,11 +131,14 @@ class WindowsEventProcessor:
|
|
|
123
131
|
registry: AgentProcessRegistry | None = None,
|
|
124
132
|
book: SessionBook | None = None,
|
|
125
133
|
ignore_system: bool = True,
|
|
134
|
+
self_pid: int | None = None,
|
|
126
135
|
) -> None:
|
|
127
136
|
self._agent_user = agent_user.lower() if agent_user else None
|
|
128
137
|
self._registry = registry or AgentProcessRegistry()
|
|
129
138
|
self._book = book or SessionBook()
|
|
130
139
|
self._ignore_system = ignore_system
|
|
140
|
+
self._self_pid = os.getpid() if self_pid is None else self_pid
|
|
141
|
+
self._own: OrderedDict[int, None] = OrderedDict() # pids of our own polls + children
|
|
131
142
|
self._ppid: OrderedDict[int, int] = OrderedDict()
|
|
132
143
|
self._logons: dict[str, str] = {} # logon id → session key
|
|
133
144
|
self._kinds: dict[str, SessionKind] = {} # logon id → login kind
|
|
@@ -202,6 +213,9 @@ class WindowsEventProcessor:
|
|
|
202
213
|
d = ev.data
|
|
203
214
|
pid, ppid = _hex(d.get("NewProcessId")), _hex(d.get("ProcessId"))
|
|
204
215
|
self._remember_ppid(pid, ppid)
|
|
216
|
+
image = d.get("NewProcessName", "")
|
|
217
|
+
if self._is_own_poll(pid, ppid, image):
|
|
218
|
+
return
|
|
205
219
|
logon_id = d.get("SubjectLogonId", "").lower()
|
|
206
220
|
if self._ignore_system and logon_id in _SYSTEM_LOGON_IDS:
|
|
207
221
|
return
|
|
@@ -232,7 +246,6 @@ class WindowsEventProcessor:
|
|
|
232
246
|
session_key, started=ev.time, verdict=verdict, os_user=user,
|
|
233
247
|
).agent_run_id
|
|
234
248
|
|
|
235
|
-
image = d.get("NewProcessName", "")
|
|
236
249
|
recorder.record_event(ActivityEvent(
|
|
237
250
|
os=Os.WINDOWS, source=Source.WIN_EVENTLOG, category=Category.COMMAND, action="exec",
|
|
238
251
|
occurredAt=ev.time, sessionKey=session_key,
|
|
@@ -243,6 +256,20 @@ class WindowsEventProcessor:
|
|
|
243
256
|
metadata={"parent": d.get("ParentProcessName") or None},
|
|
244
257
|
))
|
|
245
258
|
|
|
259
|
+
def _is_own_poll(self, pid: int, ppid: int, image: str) -> bool:
|
|
260
|
+
"""A ``wevtutil`` this process spawned to poll, or a child of one (its ``conhost``).
|
|
261
|
+
|
|
262
|
+
Matched by parent pid AND image name, so anything else the panel spawns —
|
|
263
|
+
an agent run, ``git``, ``claude`` — is still recorded.
|
|
264
|
+
"""
|
|
265
|
+
name = image.rsplit("\\", 1)[-1].lower()
|
|
266
|
+
if not ((ppid == self._self_pid and name in _OWN_TOOLS) or ppid in self._own):
|
|
267
|
+
return False
|
|
268
|
+
self._own[pid] = None
|
|
269
|
+
while len(self._own) > _MAX_OWN_PIDS:
|
|
270
|
+
self._own.popitem(last=False)
|
|
271
|
+
return True
|
|
272
|
+
|
|
246
273
|
def _remember_ppid(self, pid: int, ppid: int) -> None:
|
|
247
274
|
self._ppid[pid] = ppid
|
|
248
275
|
self._ppid.move_to_end(pid)
|
|
@@ -9,7 +9,9 @@ Per platform:
|
|
|
9
9
|
``loginctl enable-linger`` so it runs without an active login.
|
|
10
10
|
``Restart=always`` gives crash-recovery; ``enable`` gives boot-start.
|
|
11
11
|
* macOS — a launchd LaunchAgent with ``RunAtLoad`` + ``KeepAlive``.
|
|
12
|
-
* Windows — a Scheduled Task (``schtasks``) that runs the panel at logon
|
|
12
|
+
* Windows — a Scheduled Task (``schtasks``) that runs the panel at logon, plus a
|
|
13
|
+
second, daily task that runs ``agentos-auto update`` (the systemd
|
|
14
|
+
timer's counterpart — without it a Windows box never updated itself).
|
|
13
15
|
|
|
14
16
|
Everything is best-effort and returns ``(ok, message)`` so the caller (the
|
|
15
17
|
``agentos-ui install`` command and the bootstrap scripts) can report cleanly.
|
|
@@ -28,6 +30,7 @@ IS_MAC = sys.platform == "darwin"
|
|
|
28
30
|
|
|
29
31
|
SERVICE_NAME = "agentos-ui" # systemd unit / launchd label base
|
|
30
32
|
WIN_TASK = "AgentOS Control Panel" # Windows scheduled-task name
|
|
33
|
+
WIN_UPDATE_TASK = "AgentOS Self-Update" # Windows counterpart of agentos-update.timer
|
|
31
34
|
|
|
32
35
|
|
|
33
36
|
def _exe() -> str:
|
|
@@ -166,7 +169,7 @@ def _auto_exe() -> str:
|
|
|
166
169
|
minimal environment, and picking up a different venv's console script is how
|
|
167
170
|
an updater ends up upgrading the wrong installation.
|
|
168
171
|
"""
|
|
169
|
-
candidate = Path(sys.executable).parent / "agentos-auto"
|
|
172
|
+
candidate = Path(sys.executable).parent / ("agentos-auto.exe" if IS_WINDOWS else "agentos-auto")
|
|
170
173
|
if candidate.exists():
|
|
171
174
|
return str(candidate)
|
|
172
175
|
return shutil.which("agentos-auto") or "agentos-auto"
|
|
@@ -174,6 +177,8 @@ def _auto_exe() -> str:
|
|
|
174
177
|
|
|
175
178
|
def update_timer_installed() -> bool:
|
|
176
179
|
"""Is the daily self-update timer present on this box?"""
|
|
180
|
+
if IS_WINDOWS:
|
|
181
|
+
return _run(["schtasks", "/Query", "/TN", WIN_UPDATE_TASK])[0] == 0
|
|
177
182
|
if not _systemd_available():
|
|
178
183
|
return False
|
|
179
184
|
is_root = hasattr(os, "geteuid") and os.geteuid() == 0
|
|
@@ -185,6 +190,8 @@ def update_timer_installed() -> bool:
|
|
|
185
190
|
def _install_update_timer() -> tuple[bool, str]:
|
|
186
191
|
"""Install (or refresh) the daily self-update timer. Best-effort by design —
|
|
187
192
|
the panel must install even on a box where the timer cannot."""
|
|
193
|
+
if IS_WINDOWS:
|
|
194
|
+
return _install_update_schtask()
|
|
188
195
|
if not _systemd_available():
|
|
189
196
|
return False, "no systemd — skipping the self-update timer"
|
|
190
197
|
home = str(Path.home())
|
|
@@ -378,7 +385,82 @@ def _install_schtask(host: str, port: int) -> tuple[bool, str]:
|
|
|
378
385
|
f"'Run whether user is logged on' + 'Restart on failure' in Task Scheduler.")
|
|
379
386
|
|
|
380
387
|
|
|
388
|
+
def _update_task_xml(exe: str, home: str) -> str:
|
|
389
|
+
"""Task Scheduler XML for the daily self-update — the systemd timer, translated.
|
|
390
|
+
|
|
391
|
+
XML rather than ``schtasks`` flags because the flags cannot express the two
|
|
392
|
+
settings that matter most: ``RandomDelay`` (the fleet must not clone from GitHub
|
|
393
|
+
in the same second) and ``StartWhenAvailable`` (a box that was off at the
|
|
394
|
+
scheduled time updates when it is next up — systemd's ``Persistent=true``).
|
|
395
|
+
|
|
396
|
+
It runs as the installing user with the highest available privileges, same as
|
|
397
|
+
the panel task: ``~/.agentos`` (token, ``.env``) lives in that user's profile, and
|
|
398
|
+
the updater's activity-audit step needs an elevated token to configure auditpol.
|
|
399
|
+
"""
|
|
400
|
+
from xml.sax.saxutils import escape
|
|
401
|
+
|
|
402
|
+
delay_min = max(1, UPDATE_RANDOM_DELAY_S // 60)
|
|
403
|
+
return (
|
|
404
|
+
'<?xml version="1.0" encoding="UTF-16"?>\n'
|
|
405
|
+
'<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">\n'
|
|
406
|
+
" <RegistrationInfo><Description>AgentOS SDK self-update (daily)</Description>"
|
|
407
|
+
"</RegistrationInfo>\n"
|
|
408
|
+
" <Triggers>\n"
|
|
409
|
+
" <CalendarTrigger>\n"
|
|
410
|
+
" <StartBoundary>2026-01-01T03:00:00</StartBoundary>\n"
|
|
411
|
+
f" <RandomDelay>PT{delay_min}M</RandomDelay>\n"
|
|
412
|
+
" <ScheduleByDay><DaysInterval>1</DaysInterval></ScheduleByDay>\n"
|
|
413
|
+
" </CalendarTrigger>\n"
|
|
414
|
+
" </Triggers>\n"
|
|
415
|
+
" <Principals><Principal id=\"Author\">"
|
|
416
|
+
"<LogonType>InteractiveToken</LogonType><RunLevel>HighestAvailable</RunLevel>"
|
|
417
|
+
"</Principal></Principals>\n"
|
|
418
|
+
" <Settings>\n"
|
|
419
|
+
" <StartWhenAvailable>true</StartWhenAvailable>\n"
|
|
420
|
+
" <RunOnlyIfNetworkAvailable>true</RunOnlyIfNetworkAvailable>\n"
|
|
421
|
+
" <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>\n"
|
|
422
|
+
" <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>\n"
|
|
423
|
+
" <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>\n"
|
|
424
|
+
" <ExecutionTimeLimit>PT1H</ExecutionTimeLimit>\n"
|
|
425
|
+
" </Settings>\n"
|
|
426
|
+
' <Actions Context="Author"><Exec>'
|
|
427
|
+
f"<Command>{escape(exe)}</Command><Arguments>update</Arguments>"
|
|
428
|
+
f"<WorkingDirectory>{escape(home)}</WorkingDirectory>"
|
|
429
|
+
"</Exec></Actions>\n"
|
|
430
|
+
"</Task>\n"
|
|
431
|
+
)
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def _install_update_schtask() -> tuple[bool, str]:
|
|
435
|
+
"""Create (or replace) the daily self-update task. Best-effort, like the timer.
|
|
436
|
+
|
|
437
|
+
A SEPARATE task from the panel's for the same reason the systemd updater is a
|
|
438
|
+
separate unit: the update restarts the panel task, and an updater running inside
|
|
439
|
+
it would be ended part way through its own ``pip install``.
|
|
440
|
+
"""
|
|
441
|
+
import tempfile
|
|
442
|
+
|
|
443
|
+
xml = _update_task_xml(_auto_exe(), str(Path.home()))
|
|
444
|
+
fd, path = tempfile.mkstemp(suffix=".xml")
|
|
445
|
+
try:
|
|
446
|
+
with os.fdopen(fd, "w", encoding="utf-16") as fh: # schtasks /XML wants UTF-16
|
|
447
|
+
fh.write(xml)
|
|
448
|
+
rc, out = _run(["schtasks", "/Create", "/TN", WIN_UPDATE_TASK, "/XML", path, "/F"])
|
|
449
|
+
except OSError as exc:
|
|
450
|
+
return False, f"could not write the self-update task: {exc}"
|
|
451
|
+
finally:
|
|
452
|
+
try:
|
|
453
|
+
os.unlink(path)
|
|
454
|
+
except OSError:
|
|
455
|
+
pass
|
|
456
|
+
if rc != 0:
|
|
457
|
+
return False, f"could not create the self-update task:\n{out}"
|
|
458
|
+
return True, (f"self-update task '{WIN_UPDATE_TASK}' created — "
|
|
459
|
+
f"daily, randomized, catches up after downtime")
|
|
460
|
+
|
|
461
|
+
|
|
381
462
|
def _uninstall_schtask() -> tuple[bool, str]:
|
|
463
|
+
_run(["schtasks", "/Delete", "/TN", WIN_UPDATE_TASK, "/F"])
|
|
382
464
|
_run(["schtasks", "/End", "/TN", WIN_TASK])
|
|
383
465
|
rc, out = _run(["schtasks", "/Delete", "/TN", WIN_TASK, "/F"])
|
|
384
466
|
return (rc == 0), (f"removed scheduled task '{WIN_TASK}'." if rc == 0 else out)
|
|
@@ -388,7 +470,9 @@ def _uninstall_schtask() -> tuple[bool, str]:
|
|
|
388
470
|
|
|
389
471
|
def install(host: str = "0.0.0.0", port: int = 8765) -> tuple[bool, str]:
|
|
390
472
|
if IS_WINDOWS:
|
|
391
|
-
|
|
473
|
+
ok, msg = _install_schtask(host, port)
|
|
474
|
+
timer_ok, timer_msg = _install_update_timer()
|
|
475
|
+
return ok, f"{msg}\n {'' if timer_ok else 'note: '}{timer_msg}"
|
|
392
476
|
if IS_MAC:
|
|
393
477
|
return _install_launchd(host, port)
|
|
394
478
|
if _systemd_available():
|
|
@@ -417,6 +417,36 @@ class TestWindowsProcessing:
|
|
|
417
417
|
assert e["sessionKey"] == "agent-run:RUN1" and e["agentRunId"] == "RUN1"
|
|
418
418
|
assert (e["actorType"], e["actorConfidence"]) == ("AGENT", 95)
|
|
419
419
|
|
|
420
|
+
def test_the_collectors_own_wevtutil_polls_are_not_recorded(self):
|
|
421
|
+
"""Every poll spawns wevtutil from the panel (as the panel's user, not SYSTEM);
|
|
422
|
+
recording it would add ~8,600 self-referential events a day per VPS."""
|
|
423
|
+
_win(
|
|
424
|
+
_xml(4688, 1, NewProcessId="0x300", ProcessId="0x50", SubjectUserName="Administrator",
|
|
425
|
+
SubjectLogonId="0xabc", NewProcessName="C:\\Windows\\System32\\wevtutil.exe",
|
|
426
|
+
CommandLine="wevtutil qe Security /f:xml"),
|
|
427
|
+
# its console host is a child of the wevtutil, not of the panel
|
|
428
|
+
_xml(4688, 2, NewProcessId="0x301", ProcessId="0x300", SubjectUserName="Administrator",
|
|
429
|
+
SubjectLogonId="0xabc", NewProcessName="C:\\Windows\\System32\\conhost.exe"),
|
|
430
|
+
self_pid=0x50,
|
|
431
|
+
)
|
|
432
|
+
assert _queued() == ([], [])
|
|
433
|
+
|
|
434
|
+
def test_other_children_of_the_panel_are_still_recorded(self):
|
|
435
|
+
"""Only the poll tool is filtered — an agent run or git the panel spawns is not."""
|
|
436
|
+
_win(_xml(4688, 1, NewProcessId="0x300", ProcessId="0x50", SubjectUserName="Administrator",
|
|
437
|
+
SubjectLogonId="0xabc", NewProcessName="C:\\Program Files\\Git\\cmd\\git.exe"),
|
|
438
|
+
self_pid=0x50)
|
|
439
|
+
_, events = _queued()
|
|
440
|
+
assert [e["processName"] for e in events] == ["git.exe"]
|
|
441
|
+
|
|
442
|
+
def test_a_wevtutil_someone_else_runs_is_recorded(self):
|
|
443
|
+
"""A person auditing the box by hand is activity; only OUR polls are noise."""
|
|
444
|
+
_win(_xml(4688, 1, NewProcessId="0x300", ProcessId="0x77", SubjectUserName="alice",
|
|
445
|
+
SubjectLogonId="0xabc", NewProcessName="C:\\Windows\\System32\\wevtutil.exe"),
|
|
446
|
+
self_pid=0x50)
|
|
447
|
+
_, events = _queued()
|
|
448
|
+
assert [e["processName"] for e in events] == ["wevtutil.exe"]
|
|
449
|
+
|
|
420
450
|
def test_a_failed_logon_is_a_warning_event(self):
|
|
421
451
|
_win(_xml(4625, 1, TargetUserName="admin", IpAddress="198.51.100.9"))
|
|
422
452
|
_, events = _queued()
|
|
@@ -134,3 +134,97 @@ class TestInstallation:
|
|
|
134
134
|
ok, msg = service.install()
|
|
135
135
|
assert ok is True
|
|
136
136
|
assert "panel ok" in msg and "timer broke" in msg
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
class TestWindowsUpdateTask:
|
|
140
|
+
"""The Windows counterpart of the timer. Without it a Windows VPS only ever
|
|
141
|
+
updated when someone ran `agentos-auto update` by hand — so a feature shipped to
|
|
142
|
+
the fleet (the activity audit trail) silently never reached those boxes."""
|
|
143
|
+
|
|
144
|
+
_NS = "{http://schemas.microsoft.com/windows/2004/02/mit/task}"
|
|
145
|
+
|
|
146
|
+
def _xml(self, exe=r"C:\Users\Administrator\agentsystem\venv\Scripts\agentos-auto.exe"):
|
|
147
|
+
import xml.etree.ElementTree as ET
|
|
148
|
+
return ET.fromstring(service._update_task_xml(exe, r"C:\Users\Administrator")
|
|
149
|
+
.split("\n", 1)[1]) # ET rejects the UTF-16 declaration on a str
|
|
150
|
+
|
|
151
|
+
def test_runs_agentos_auto_update(self):
|
|
152
|
+
ex = self._xml().find(f"{self._NS}Actions/{self._NS}Exec")
|
|
153
|
+
assert ex.findtext(f"{self._NS}Command").endswith("agentos-auto.exe")
|
|
154
|
+
assert ex.findtext(f"{self._NS}Arguments") == "update"
|
|
155
|
+
assert ex.findtext(f"{self._NS}WorkingDirectory") == r"C:\Users\Administrator"
|
|
156
|
+
|
|
157
|
+
def test_daily_and_randomized_like_the_timer(self):
|
|
158
|
+
trig = self._xml().find(f"{self._NS}Triggers/{self._NS}CalendarTrigger")
|
|
159
|
+
assert trig.findtext(f"{self._NS}ScheduleByDay/{self._NS}DaysInterval") == "1"
|
|
160
|
+
assert trig.findtext(f"{self._NS}RandomDelay") == f"PT{service.UPDATE_RANDOM_DELAY_S // 60}M"
|
|
161
|
+
|
|
162
|
+
def test_catches_up_after_downtime(self):
|
|
163
|
+
"""systemd's Persistent=true: a box that was off at 03:00 updates when next up."""
|
|
164
|
+
assert self._xml().findtext(f"{self._NS}Settings/{self._NS}StartWhenAvailable") == "true"
|
|
165
|
+
|
|
166
|
+
def test_runs_elevated_as_the_user(self):
|
|
167
|
+
"""Elevated so the updater's audit step can run auditpol; as the user so
|
|
168
|
+
~/.agentos (token, .env) is the right profile."""
|
|
169
|
+
pr = self._xml().find(f"{self._NS}Principals/{self._NS}Principal")
|
|
170
|
+
assert pr.findtext(f"{self._NS}RunLevel") == "HighestAvailable"
|
|
171
|
+
assert pr.findtext(f"{self._NS}LogonType") == "InteractiveToken"
|
|
172
|
+
|
|
173
|
+
def test_a_path_with_xml_metacharacters_is_escaped(self):
|
|
174
|
+
exe = r"C:\R&D <x>\agentos-auto.exe"
|
|
175
|
+
assert self._xml(exe).find(
|
|
176
|
+
f"{self._NS}Actions/{self._NS}Exec/{self._NS}Command").text == exe
|
|
177
|
+
|
|
178
|
+
def test_creates_the_task_from_a_utf16_xml_file(self, monkeypatch):
|
|
179
|
+
monkeypatch.setattr(service, "IS_WINDOWS", True)
|
|
180
|
+
seen = {}
|
|
181
|
+
|
|
182
|
+
def fake_run(argv):
|
|
183
|
+
seen["argv"] = argv
|
|
184
|
+
seen["xml"] = Path(argv[argv.index("/XML") + 1]).read_text(encoding="utf-16")
|
|
185
|
+
return 0, "SUCCESS"
|
|
186
|
+
monkeypatch.setattr(service, "_run", fake_run)
|
|
187
|
+
|
|
188
|
+
ok, msg = service._install_update_timer()
|
|
189
|
+
assert ok is True and service.WIN_UPDATE_TASK in msg
|
|
190
|
+
assert seen["argv"][:4] == ["schtasks", "/Create", "/TN", service.WIN_UPDATE_TASK]
|
|
191
|
+
assert seen["argv"][-1] == "/F"
|
|
192
|
+
assert "<Arguments>update</Arguments>" in seen["xml"]
|
|
193
|
+
assert not Path(seen["argv"][seen["argv"].index("/XML") + 1]).exists() # temp file removed
|
|
194
|
+
|
|
195
|
+
def test_a_failed_create_is_reported_not_raised(self, monkeypatch):
|
|
196
|
+
monkeypatch.setattr(service, "IS_WINDOWS", True)
|
|
197
|
+
monkeypatch.setattr(service, "_run", lambda argv: (1, "Access is denied."))
|
|
198
|
+
ok, msg = service._install_update_timer()
|
|
199
|
+
assert ok is False and "Access is denied." in msg
|
|
200
|
+
|
|
201
|
+
def test_detection_queries_the_task(self, monkeypatch):
|
|
202
|
+
monkeypatch.setattr(service, "IS_WINDOWS", True)
|
|
203
|
+
calls = []
|
|
204
|
+
monkeypatch.setattr(service, "_run", lambda argv: (calls.append(argv), (0, ""))[1])
|
|
205
|
+
assert service.update_timer_installed() is True
|
|
206
|
+
assert calls == [["schtasks", "/Query", "/TN", service.WIN_UPDATE_TASK]]
|
|
207
|
+
monkeypatch.setattr(service, "_run", lambda argv: (1, "cannot find"))
|
|
208
|
+
assert service.update_timer_installed() is False
|
|
209
|
+
|
|
210
|
+
def test_install_creates_both_tasks_and_a_timer_failure_is_not_fatal(self, monkeypatch):
|
|
211
|
+
monkeypatch.setattr(service, "IS_WINDOWS", True)
|
|
212
|
+
monkeypatch.setattr(service, "_install_schtask", lambda h, p: (True, "panel ok"))
|
|
213
|
+
monkeypatch.setattr(service, "_install_update_timer", lambda: (False, "timer broke"))
|
|
214
|
+
ok, msg = service.install()
|
|
215
|
+
assert ok is True and "panel ok" in msg and "timer broke" in msg
|
|
216
|
+
|
|
217
|
+
def test_uninstall_removes_the_update_task_too(self, monkeypatch):
|
|
218
|
+
monkeypatch.setattr(service, "IS_WINDOWS", True)
|
|
219
|
+
calls = []
|
|
220
|
+
monkeypatch.setattr(service, "_run", lambda argv: (calls.append(argv), (0, ""))[1])
|
|
221
|
+
service.uninstall()
|
|
222
|
+
assert ["schtasks", "/Delete", "/TN", service.WIN_UPDATE_TASK, "/F"] in calls
|
|
223
|
+
assert ["schtasks", "/Delete", "/TN", service.WIN_TASK, "/F"] in calls
|
|
224
|
+
|
|
225
|
+
def test_resolves_the_exe_console_script_on_windows(self, monkeypatch, tmp_path):
|
|
226
|
+
"""A venv's Scripts dir holds agentos-auto.EXE; the bare name never exists there."""
|
|
227
|
+
(tmp_path / "agentos-auto.exe").write_text("")
|
|
228
|
+
monkeypatch.setattr(service, "IS_WINDOWS", True)
|
|
229
|
+
monkeypatch.setattr(service.sys, "executable", str(tmp_path / "python.exe"))
|
|
230
|
+
assert service._auto_exe() == str(tmp_path / "agentos-auto.exe")
|
|
File without changes
|
|
File without changes
|
{devops_bot_sdk-1.6.44 → devops_bot_sdk-1.6.46}/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
|