devops-bot-sdk 1.6.56__tar.gz → 1.6.57__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.56 → devops_bot_sdk-1.6.57}/PKG-INFO +1 -1
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/__init__.py +1 -1
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/cli.py +94 -1
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ipc/handlers.py +4 -6
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/local_exec.py +159 -13
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/plan_run.py +2 -3
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/run_auto.py +2 -3
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ui/setup_manager.py +14 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_fallback_cli.py +126 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_task_file_and_plan_run.py +14 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/README.md +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/devops_bot_sdk.egg-info/SOURCES.txt +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/devops_bot_sdk.egg-info/requires.txt +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/devops_bot_sdk.egg-info/top_level.txt +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/pyproject.toml +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/activity/__init__.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/activity/classifier.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/activity/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/activity/collectors/common.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/activity/collectors/linux_auditd.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/activity/collectors/macos_eslogger.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/activity/collectors/windows_eventlog.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/activity/models.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/activity/os_setup.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/activity/recorder.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/activity/redact.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/activity/settings.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/activity/shipper.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/activity/spool.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/agentd.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/agents/__init__.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/agents/activity.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/agents/builder.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/agents/chat.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/agents/connectors.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/agents/engines/__init__.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/agents/engines/claude_code.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/agents/engines/http.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/agents/engines/mcp.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/agents/engines/n8n.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/agents/engines/shell.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/agents/integrations.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/agents/inventory.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/agents/library/__init__.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/agents/manifest.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/agents/prompt_studio.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/agents/prompt_template.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/agents/prompt_vars.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/agents/run_log.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/agents/schedules.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/agents/secrets.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/agents/ticket_run.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/agents/usage.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/agents/varstore.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/browser_profile.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/client.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/collectors/files.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/collectors/process.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/collectors/screenshot.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/config.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/crucial.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/departments.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/design_verify/__init__.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/design_verify/assets.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/design_verify/bootstrap.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/design_verify/browser.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/design_verify/compare.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/design_verify/loop.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/estimator.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/exceptions.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/firewall.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/formats.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/git_ops.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/github_fetch.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/github_ingest.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/graph_recon.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/graphify.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/hooks/__init__.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/hooks/crucial_guard.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/intake.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ipc/__init__.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ipc/electron_bridge.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/journal.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/memory_guard.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/models/__init__.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/models/envelope.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/models/requests.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/models/responses.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/models/snapshots.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/phase_timer.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/platform_compat.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/prd_progress.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/proof_readers.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/providers.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/py.typed +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/resume.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/router.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/scheduler.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/self_report.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/self_report_local.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/sse.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/swagger.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/task_file.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/task_types.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/test.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/test_pipeline.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ticket_fields.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/tiers/__init__.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/tiers/mini_agent.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ui/__init__.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ui/api.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ui/dashboard.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ui/health.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ui/ip_allowlist.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ui/jobs.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ui/models.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ui/runner.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ui/security.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ui/server.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ui/service.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ui/session.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ui/static/agents.html +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ui/static/app.js +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ui/static/chat.html +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ui/static/custom-agents.html +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ui/static/default-agents.html +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ui/static/index.html +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ui/static/report.html +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ui/static/runs.html +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ui/static/schedules.html +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ui/static/setup.html +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ui/static/styles.css +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ui/static/tickets.html +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/ui/swagger.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/updater.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/url_access.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/vendor/aidraftsman-cli-0.1.0.tgz +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/vps_health.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/sdk/write_hook.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/setup.cfg +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_activity_collectors.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_activity_core.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_activity_macos.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_activity_os_setup.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_activity_pipeline.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_agent_activity.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_agent_activity_tracking.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_agent_chat_features.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_agent_listing.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_agent_prompt_async.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_agent_prompt_models.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_agent_run_ticket.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_agent_schedules.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_agent_workspace_settings.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_agents_connectors_builder.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_allowed_tools_config.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_auto_agent_dispatch.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_browser_profile.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_cheap_tier.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_client_jira_projects.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_config_token_diagnostics.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_configure_token_owner.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_console_encoding.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_crash_recovery.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_departments.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_done_transition.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_epic_decompose_run.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_epic_inherited_agent_id.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_epic_session_isolation.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_estimator.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_firewall_open_on_update.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_formats.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_git_action.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_github_fetch.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_github_ingest.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_graph_recon.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_graphify_build.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_graphify_hook_install.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_health.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_intake_job_outcome.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_intake_plan_review.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_journal.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_local_exec_provider.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_mcp_route_contract.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_memory_guard.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_phase_timer.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_plan_new_command.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_plan_run_activity.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_pr_identity.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_prd_artifact.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_project_path_resolution.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_proof_readers.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_providers.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_recon_session_isolation.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_resume.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_router.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_run_auto_agent_only.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_run_outputs.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_run_verdict_is_honest.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_schedule_api.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_schedule_outputs_and_plans.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_scheduler.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_self_report.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_service_token_warning.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_stale_panel_restart.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_swagger_command.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_task_file_format_doc.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_ticket_fields.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_ticket_run_resolve_by_id.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_ticket_speed_fixes.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_tier_flag_parity.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_ui_gate.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_ui_lifespan.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_update_timer.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_updater_extras.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_updater_pep668.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_updater_reports_the_truth.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_url_access.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_vps_health.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_windows_loop_identity.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/tests/test_write_hook.py +0 -0
|
@@ -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.57" # 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
|
|
@@ -104,6 +104,11 @@ Command surface:
|
|
|
104
104
|
One submission in full — its text, its proofs and the evaluator's
|
|
105
105
|
per-criterion reasoning.
|
|
106
106
|
|
|
107
|
+
agentos fallback install | login [--browser | --api-key [KEY]] | status [--verify] | logout
|
|
108
|
+
The AI Draftsman CLI a ticket re-runs on when `claude` is not working:
|
|
109
|
+
install the bundled copy, log it in (a sign-in code approved on the
|
|
110
|
+
frontend, or an API key), and check it. See sdk.local_exec.fallback_status.
|
|
111
|
+
|
|
107
112
|
agentos agentd start [--foreground]
|
|
108
113
|
Start the local agent daemon (background by default).
|
|
109
114
|
agentos agentd status
|
|
@@ -188,12 +193,16 @@ def main() -> None:
|
|
|
188
193
|
_browser_command(rest)
|
|
189
194
|
return
|
|
190
195
|
|
|
196
|
+
if noun == "fallback":
|
|
197
|
+
_fallback_command(rest)
|
|
198
|
+
return
|
|
199
|
+
|
|
191
200
|
_print_usage()
|
|
192
201
|
sys.exit(1)
|
|
193
202
|
|
|
194
203
|
|
|
195
204
|
def _print_usage() -> None:
|
|
196
|
-
print("usage: agentos <configure|chat|agent|integration|default-agents|agentd|github|report|browser|firewall|activity-setup|swagger> ...")
|
|
205
|
+
print("usage: agentos <configure|chat|agent|integration|default-agents|agentd|github|report|browser|fallback|firewall|activity-setup|swagger> ...")
|
|
197
206
|
print()
|
|
198
207
|
print(" agentos configure [--rotate]")
|
|
199
208
|
print(" agentos chat [--product-id ID] [--pv-id ID] [--no-autorun] # requirements → ticket")
|
|
@@ -205,6 +214,7 @@ def _print_usage() -> None:
|
|
|
205
214
|
print(" agentos github <ingest [--days N]|status> # PR ingestion; --days N backfills N days")
|
|
206
215
|
print(" agentos report <submit|list|show ID> # report work Jira/GitHub never saw")
|
|
207
216
|
print(" agentos browser <login URL|status|logout> # sign in once; private links become readable")
|
|
217
|
+
print(" agentos fallback <install|login|status|logout> # the AI Draftsman CLI used when claude is not working")
|
|
208
218
|
print(" agentos firewall [--apply] [--revoke IP] [--port N] # open the UI port to the allowlisted IPs")
|
|
209
219
|
print(" agentos firewall --any [--apply] # open the UI port to any source (allowlist is the gate)")
|
|
210
220
|
print(" agentos swagger [--open] [--json [PATH]] # where /docs is, and can you reach it")
|
|
@@ -892,6 +902,89 @@ def _browser_command(args: list[str]) -> None:
|
|
|
892
902
|
sys.exit(1)
|
|
893
903
|
|
|
894
904
|
|
|
905
|
+
# ── fallback ─────────────────────────────────────────────────────────────
|
|
906
|
+
|
|
907
|
+
def _fallback_usage() -> None:
|
|
908
|
+
print("usage: agentos fallback install # npm-install the bundled CLI now")
|
|
909
|
+
print(" agentos fallback login [--browser | --api-key [KEY]] [--base-url URL] [--no-browser]")
|
|
910
|
+
print(" agentos fallback status [--verify] # --verify asks the server too")
|
|
911
|
+
print(" agentos fallback logout")
|
|
912
|
+
print()
|
|
913
|
+
print(" When the `claude` CLI is not working, tickets re-run on the AI Draftsman CLI")
|
|
914
|
+
print(" bundled with the SDK. It needs its own login, made as the user the SDK runs as.")
|
|
915
|
+
print()
|
|
916
|
+
print(" --browser prints a sign-in code and a link; approve it on the AI Draftsman")
|
|
917
|
+
print(" frontend from any machine — works on a headless VPS")
|
|
918
|
+
print(" --api-key paste a key from Settings → API keys (hidden prompt), or pass")
|
|
919
|
+
print(" KEY after the flag")
|
|
920
|
+
print(" (neither) the CLI's own menu")
|
|
921
|
+
|
|
922
|
+
|
|
923
|
+
def _fallback_command(args: list[str]) -> None:
|
|
924
|
+
"""``agentos fallback install|login|status|logout`` — the AI Draftsman fallback CLI."""
|
|
925
|
+
from sdk import local_exec as le
|
|
926
|
+
|
|
927
|
+
verb = args[0] if args else None
|
|
928
|
+
rest = args[1:]
|
|
929
|
+
|
|
930
|
+
if verb == "install":
|
|
931
|
+
if le._fallback_disabled():
|
|
932
|
+
print("error: the fallback is disabled (AGENTOS_FALLBACK_CLI=off)")
|
|
933
|
+
sys.exit(1)
|
|
934
|
+
cli = asyncio.run(le.ensure_fallback_cli())
|
|
935
|
+
if not cli:
|
|
936
|
+
print("error: could not install the AI Draftsman CLI (Node.js 20+ and npm are needed)")
|
|
937
|
+
sys.exit(1)
|
|
938
|
+
print(f"installed: {cli}")
|
|
939
|
+
ad = Path(cli).with_name("ad.cmd" if cli.lower().endswith(".cmd") else "ad")
|
|
940
|
+
if ad.exists():
|
|
941
|
+
print(f"also as : {ad} (the same CLI under its short name)")
|
|
942
|
+
print("next : agentos fallback login")
|
|
943
|
+
return
|
|
944
|
+
|
|
945
|
+
if verb == "login":
|
|
946
|
+
method = api_key = base_url = None
|
|
947
|
+
no_browser = True if "--no-browser" in rest else None
|
|
948
|
+
if "--browser" in rest:
|
|
949
|
+
method = "browser"
|
|
950
|
+
if "--api-key" in rest:
|
|
951
|
+
method = "api-key"
|
|
952
|
+
i = rest.index("--api-key") + 1
|
|
953
|
+
if i < len(rest) and not rest[i].startswith("-"):
|
|
954
|
+
api_key = rest[i]
|
|
955
|
+
if "--base-url" in rest:
|
|
956
|
+
try:
|
|
957
|
+
base_url = rest[rest.index("--base-url") + 1]
|
|
958
|
+
except IndexError:
|
|
959
|
+
print("error: --base-url needs a URL")
|
|
960
|
+
sys.exit(1)
|
|
961
|
+
rc = le.fallback_login(method=method, api_key=api_key, base_url=base_url,
|
|
962
|
+
no_browser=no_browser)
|
|
963
|
+
if rc == 0:
|
|
964
|
+
print("check it : agentos fallback status --verify")
|
|
965
|
+
sys.exit(rc)
|
|
966
|
+
|
|
967
|
+
if verb == "status":
|
|
968
|
+
st = le.fallback_status(verify="--verify" in rest)
|
|
969
|
+
print(f"installed : {'yes' if st['installed'] else 'no'}")
|
|
970
|
+
print(f"path : {st['path'] or '-'}")
|
|
971
|
+
print(f"logged in : {'yes' if st['logged_in'] else 'no'}")
|
|
972
|
+
if st["verified"] is not None:
|
|
973
|
+
print(f"server : {'key accepted' if st['verified'] else 'NOT accepted'}")
|
|
974
|
+
print(f"account : {st['account'] or '-'}")
|
|
975
|
+
print(f"server url: {st['base_url'] or '-'}")
|
|
976
|
+
if st["error"]:
|
|
977
|
+
print(f"problem : {st['error']}")
|
|
978
|
+
ready = st["logged_in"] and st["verified"] is not False
|
|
979
|
+
sys.exit(0 if ready else 1)
|
|
980
|
+
|
|
981
|
+
if verb == "logout":
|
|
982
|
+
sys.exit(le.fallback_logout())
|
|
983
|
+
|
|
984
|
+
_fallback_usage()
|
|
985
|
+
sys.exit(1)
|
|
986
|
+
|
|
987
|
+
|
|
895
988
|
def _report_check(url: str) -> None:
|
|
896
989
|
"""``agentos report check <URL>`` — can this machine open that link?
|
|
897
990
|
|
|
@@ -1973,12 +1973,11 @@ async def _run_local_pipeline(
|
|
|
1973
1973
|
thread_id = str(payload.get("thread_id", "") or "")
|
|
1974
1974
|
project_path = str(payload.get("project_path", "") or "").strip()
|
|
1975
1975
|
|
|
1976
|
-
if not local_exec.
|
|
1976
|
+
if not local_exec.can_run_locally():
|
|
1977
1977
|
await send(Envelope(
|
|
1978
1978
|
type=Envelope.TYPE_ERROR, thread_id=thread_id,
|
|
1979
1979
|
data={"code": "CLAUDE_CLI_MISSING",
|
|
1980
|
-
"message":
|
|
1981
|
-
"Install it (and run `claude login`) to use the local claude_code engine."},
|
|
1980
|
+
"message": local_exec.NO_LOCAL_CLI_MESSAGE},
|
|
1982
1981
|
).model_dump())
|
|
1983
1982
|
return
|
|
1984
1983
|
if not project_path:
|
|
@@ -4954,12 +4953,11 @@ async def handle_orchestrate_auto_local_run(
|
|
|
4954
4953
|
# guaranteed on every exit — including early returns — via the finally below.
|
|
4955
4954
|
active_flusher: ActivityFlusher | None = None
|
|
4956
4955
|
try:
|
|
4957
|
-
if not local_exec.
|
|
4956
|
+
if not local_exec.can_run_locally():
|
|
4958
4957
|
await send(Envelope(
|
|
4959
4958
|
type=Envelope.TYPE_ERROR, thread_id=thread_id,
|
|
4960
4959
|
data={"code": "CLAUDE_CLI_MISSING",
|
|
4961
|
-
"message":
|
|
4962
|
-
"Install it and run `claude setup-token` to use local execution."},
|
|
4960
|
+
"message": local_exec.NO_LOCAL_CLI_MESSAGE},
|
|
4963
4961
|
).model_dump())
|
|
4964
4962
|
return
|
|
4965
4963
|
|
|
@@ -1052,7 +1052,9 @@ async def _run_claude_cli(
|
|
|
1052
1052
|
# prompt goes in on stdin, `-p --output-format json` prints one JSON object.
|
|
1053
1053
|
# The CLI is bundled in sdk/vendor/ and npm-installed into ~/.agentos/aidraftsman
|
|
1054
1054
|
# the first time it is needed; one already on PATH is used instead. It needs
|
|
1055
|
-
# its own login on the box
|
|
1055
|
+
# its own login on the box, as the user the SDK runs as: `agentos fallback
|
|
1056
|
+
# login` (browser sign-in code or an API key), or AD_API_KEY in the env.
|
|
1057
|
+
# `agentos fallback status --verify` checks it against the server.
|
|
1056
1058
|
#
|
|
1057
1059
|
# What it does NOT do: honour the PreToolUse approval guard, resume a Claude
|
|
1058
1060
|
# session, or stream progress events. So a resumed run never falls back (the
|
|
@@ -1108,8 +1110,8 @@ def _bundled_bin(tarball: Path) -> Path:
|
|
|
1108
1110
|
def fallback_cli_path() -> str | None:
|
|
1109
1111
|
"""Absolute path to an installed `aidraftsman` CLI, or None if absent or disabled.
|
|
1110
1112
|
|
|
1111
|
-
Resolution order: ``AGENTOS_FALLBACK_CLI``, then ``
|
|
1112
|
-
installed from the bundled tarball. Never installs anything — see
|
|
1113
|
+
Resolution order: ``AGENTOS_FALLBACK_CLI``, then ``aidraftsman`` or ``ad``
|
|
1114
|
+
on ``PATH``, then the copy installed from the bundled tarball. Never installs anything — see
|
|
1113
1115
|
:func:`ensure_fallback_cli` for that.
|
|
1114
1116
|
"""
|
|
1115
1117
|
if _fallback_disabled():
|
|
@@ -1117,9 +1119,12 @@ def fallback_cli_path() -> str | None:
|
|
|
1117
1119
|
override = _setting("AGENTOS_FALLBACK_CLI")
|
|
1118
1120
|
if override and Path(override).exists():
|
|
1119
1121
|
return override
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1122
|
+
# The package installs the same entry point under two names; a box may
|
|
1123
|
+
# only have `ad` on PATH.
|
|
1124
|
+
for name in ("aidraftsman", "ad"):
|
|
1125
|
+
found = shutil.which(name)
|
|
1126
|
+
if found:
|
|
1127
|
+
return found
|
|
1123
1128
|
tarball = _bundled_tarball()
|
|
1124
1129
|
if tarball and _bundled_bin(tarball).exists():
|
|
1125
1130
|
return str(_bundled_bin(tarball))
|
|
@@ -1164,6 +1169,145 @@ async def ensure_fallback_cli() -> str | None:
|
|
|
1164
1169
|
return await asyncio.to_thread(_install_bundled, tarball)
|
|
1165
1170
|
|
|
1166
1171
|
|
|
1172
|
+
def _fallback_argv(cli: str, *args: str) -> list[str]:
|
|
1173
|
+
# npm installs a .cmd shim on Windows — same reason as the claude spawn.
|
|
1174
|
+
cmd = [cli, *args]
|
|
1175
|
+
if sys.platform == "win32" and cli.lower().endswith((".cmd", ".bat")):
|
|
1176
|
+
cmd = [os.environ.get("COMSPEC", "cmd.exe"), "/c", *cmd]
|
|
1177
|
+
return cmd
|
|
1178
|
+
|
|
1179
|
+
|
|
1180
|
+
def fallback_status(*, verify: bool = False, timeout: float = 20.0) -> dict:
|
|
1181
|
+
"""Readiness of the AI Draftsman fallback CLI: installed AND logged in.
|
|
1182
|
+
|
|
1183
|
+
The fallback counterpart of :func:`claude_status`. ``aidraftsman auth status``
|
|
1184
|
+
only says whether a key is STORED (in ~/.config/ai-draftsman/config.json for
|
|
1185
|
+
the user running this); ``verify=True`` also asks the server, by listing
|
|
1186
|
+
agents, so an expired or revoked key shows as not logged in. AD_API_KEY in
|
|
1187
|
+
the env counts as a login, as it does for the CLI itself. Never raises.
|
|
1188
|
+
|
|
1189
|
+
Returns::
|
|
1190
|
+
|
|
1191
|
+
{"installed": bool, "logged_in": bool, "verified": bool|None,
|
|
1192
|
+
"path": str|None, "account": str|None, "base_url": str|None,
|
|
1193
|
+
"disabled": bool, "error": str|None}
|
|
1194
|
+
"""
|
|
1195
|
+
out: dict[str, Any] = {"installed": False, "logged_in": False, "verified": None,
|
|
1196
|
+
"path": None, "account": None, "base_url": None,
|
|
1197
|
+
"disabled": _fallback_disabled(), "error": None}
|
|
1198
|
+
if out["disabled"]:
|
|
1199
|
+
out["error"] = "disabled (AGENTOS_FALLBACK_CLI=off)"
|
|
1200
|
+
return out
|
|
1201
|
+
cli = fallback_cli_path()
|
|
1202
|
+
if not cli:
|
|
1203
|
+
out["error"] = "not installed — run `agentos fallback install`"
|
|
1204
|
+
return out
|
|
1205
|
+
out.update(installed=True, path=cli)
|
|
1206
|
+
try:
|
|
1207
|
+
proc = subprocess.run(_fallback_argv(cli, "auth", "status", "--json"),
|
|
1208
|
+
capture_output=True, text=True, errors="replace",
|
|
1209
|
+
timeout=timeout, check=False)
|
|
1210
|
+
data = json.loads(proc.stdout or "{}")
|
|
1211
|
+
except Exception as exc: # noqa: BLE001 — timeout / OSError / bad JSON
|
|
1212
|
+
out["error"] = f"`aidraftsman auth status` failed: {exc}"
|
|
1213
|
+
return out
|
|
1214
|
+
out["account"] = data.get("account")
|
|
1215
|
+
out["base_url"] = data.get("baseUrl")
|
|
1216
|
+
out["logged_in"] = bool(data.get("authenticated") or os.environ.get("AD_API_KEY"))
|
|
1217
|
+
if not out["logged_in"]:
|
|
1218
|
+
out["error"] = "not logged in — run `agentos fallback login`"
|
|
1219
|
+
return out
|
|
1220
|
+
if verify:
|
|
1221
|
+
try:
|
|
1222
|
+
proc = subprocess.run(_fallback_argv(cli, "agents", "list", "--json"),
|
|
1223
|
+
capture_output=True, text=True, errors="replace",
|
|
1224
|
+
timeout=timeout, check=False)
|
|
1225
|
+
except Exception as exc: # noqa: BLE001
|
|
1226
|
+
out["error"] = f"could not reach the AI Draftsman server: {exc}"
|
|
1227
|
+
return out
|
|
1228
|
+
out["verified"] = proc.returncode == 0
|
|
1229
|
+
if proc.returncode != 0:
|
|
1230
|
+
out["logged_in"] = proc.returncode != 2
|
|
1231
|
+
out["error"] = ((proc.stderr or proc.stdout).strip()[-300:]
|
|
1232
|
+
or f"aidraftsman exited with code {proc.returncode}")
|
|
1233
|
+
if proc.returncode == 2:
|
|
1234
|
+
out["error"] += " — run `agentos fallback login`"
|
|
1235
|
+
return out
|
|
1236
|
+
|
|
1237
|
+
|
|
1238
|
+
#: Shown when neither CLI can take a ticket; the entry points' "CLI missing" error.
|
|
1239
|
+
NO_LOCAL_CLI_MESSAGE = (
|
|
1240
|
+
"Neither the Claude Code CLI nor the AI Draftsman fallback is ready on this "
|
|
1241
|
+
"machine. Install claude and run `claude setup-token`, or run "
|
|
1242
|
+
"`agentos fallback install` then `agentos fallback login`."
|
|
1243
|
+
)
|
|
1244
|
+
|
|
1245
|
+
|
|
1246
|
+
def can_run_locally() -> bool:
|
|
1247
|
+
"""True when a ticket can run here: claude is installed, or the fallback is
|
|
1248
|
+
installed AND logged in.
|
|
1249
|
+
|
|
1250
|
+
The entry points (plan, the auto loop, local execution) gate on this rather
|
|
1251
|
+
than :func:`is_available`, so a box with no claude at all still runs tickets
|
|
1252
|
+
on the fallback. A missing claude fails on spawn with no session id, which
|
|
1253
|
+
is exactly what ``run_claude_local`` falls back on.
|
|
1254
|
+
"""
|
|
1255
|
+
if is_available():
|
|
1256
|
+
return True
|
|
1257
|
+
st = fallback_status()
|
|
1258
|
+
return bool(st["installed"] and st["logged_in"])
|
|
1259
|
+
|
|
1260
|
+
|
|
1261
|
+
def _headless() -> bool:
|
|
1262
|
+
"""No browser can open here: an SSH session, or Linux with no display."""
|
|
1263
|
+
if os.environ.get("SSH_CONNECTION") or os.environ.get("SSH_TTY"):
|
|
1264
|
+
return True
|
|
1265
|
+
return sys.platform.startswith("linux") and not (
|
|
1266
|
+
os.environ.get("DISPLAY") or os.environ.get("WAYLAND_DISPLAY"))
|
|
1267
|
+
|
|
1268
|
+
|
|
1269
|
+
def fallback_login(*, method: str | None = None, api_key: str | None = None,
|
|
1270
|
+
base_url: str | None = None, no_browser: bool | None = None) -> int:
|
|
1271
|
+
"""Log the fallback CLI in, interactively, on this terminal. Returns its exit code.
|
|
1272
|
+
|
|
1273
|
+
Same two ways the CLI offers: ``method="browser"`` prints a sign-in code and
|
|
1274
|
+
a link to approve on the AI Draftsman frontend (works on a headless VPS: open
|
|
1275
|
+
the link on any machine), ``method="api-key"`` validates and stores a key
|
|
1276
|
+
from Settings → API keys (prompted with hidden input when ``api_key`` is not
|
|
1277
|
+
given). ``None`` shows the CLI's own menu. Installs the bundled CLI first.
|
|
1278
|
+
"""
|
|
1279
|
+
cli = asyncio.run(ensure_fallback_cli())
|
|
1280
|
+
if not cli:
|
|
1281
|
+
print("error: the AI Draftsman CLI is not available "
|
|
1282
|
+
"(Node.js 20+ and npm are needed to install the bundled copy, "
|
|
1283
|
+
"or it is disabled with AGENTOS_FALLBACK_CLI=off)")
|
|
1284
|
+
return 1
|
|
1285
|
+
args = ["auth", "login"]
|
|
1286
|
+
if api_key:
|
|
1287
|
+
method = "api-key"
|
|
1288
|
+
args += ["--api-key", api_key]
|
|
1289
|
+
if method:
|
|
1290
|
+
args += ["--method", method]
|
|
1291
|
+
if base_url:
|
|
1292
|
+
args += ["--base-url", base_url]
|
|
1293
|
+
if no_browser is None:
|
|
1294
|
+
no_browser = _headless()
|
|
1295
|
+
if no_browser:
|
|
1296
|
+
args.append("--no-browser")
|
|
1297
|
+
try:
|
|
1298
|
+
return subprocess.run(_fallback_argv(cli, *args), check=False).returncode
|
|
1299
|
+
except KeyboardInterrupt: # the CLI already printed "Sign-in cancelled."
|
|
1300
|
+
return 130
|
|
1301
|
+
|
|
1302
|
+
|
|
1303
|
+
def fallback_logout() -> int:
|
|
1304
|
+
cli = fallback_cli_path()
|
|
1305
|
+
if not cli:
|
|
1306
|
+
print("nothing to do: the AI Draftsman CLI is not installed")
|
|
1307
|
+
return 0
|
|
1308
|
+
return subprocess.run(_fallback_argv(cli, "auth", "logout"), check=False).returncode
|
|
1309
|
+
|
|
1310
|
+
|
|
1167
1311
|
def _claude_not_working(result: dict[str, Any]) -> bool:
|
|
1168
1312
|
"""True when the run failed because of the `claude` CLI, not the task.
|
|
1169
1313
|
|
|
@@ -1213,7 +1357,8 @@ async def run_fallback_cli(
|
|
|
1213
1357
|
cli = await ensure_fallback_cli()
|
|
1214
1358
|
if not cli:
|
|
1215
1359
|
return _result(ok=False, error="AI Draftsman CLI not available (needs Node.js 20+ "
|
|
1216
|
-
"and npm to install the bundled copy
|
|
1360
|
+
"and npm to install the bundled copy; "
|
|
1361
|
+
"`agentos fallback install`).",
|
|
1217
1362
|
tier=tier, provider=provider)
|
|
1218
1363
|
|
|
1219
1364
|
path = Path(project_path).expanduser()
|
|
@@ -1222,13 +1367,11 @@ async def run_fallback_cli(
|
|
|
1222
1367
|
mode = _setting("AGENTOS_FALLBACK_PERMISSION_MODE") or "acceptEdits"
|
|
1223
1368
|
if mode not in _FALLBACK_MODES:
|
|
1224
1369
|
mode = "acceptEdits"
|
|
1225
|
-
|
|
1370
|
+
args = ["-p", "--output-format", "json", "--permission-mode", mode]
|
|
1226
1371
|
agent = _setting("AGENTOS_FALLBACK_AGENT")
|
|
1227
1372
|
if agent:
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
if sys.platform == "win32" and cli.lower().endswith((".cmd", ".bat")):
|
|
1231
|
-
cmd = [os.environ.get("COMSPEC", "cmd.exe"), "/c", *cmd]
|
|
1373
|
+
args += ["--agent", agent]
|
|
1374
|
+
cmd = _fallback_argv(cli, *args)
|
|
1232
1375
|
|
|
1233
1376
|
env = dict(os.environ)
|
|
1234
1377
|
if extra_env:
|
|
@@ -1277,8 +1420,11 @@ async def run_fallback_cli(
|
|
|
1277
1420
|
continue
|
|
1278
1421
|
if proc.returncode != 0 or not isinstance(data, dict) or "result" not in data:
|
|
1279
1422
|
err = err_text[-500:] or f"aidraftsman exited with code {proc.returncode}"
|
|
1423
|
+
auth_expired = proc.returncode == 2 or _is_auth_error(err)
|
|
1424
|
+
if auth_expired:
|
|
1425
|
+
err += " — log the fallback in with `agentos fallback login`"
|
|
1280
1426
|
return _result(ok=False, error=err, files_changed=files_changed,
|
|
1281
|
-
auth_expired=
|
|
1427
|
+
auth_expired=auth_expired,
|
|
1282
1428
|
tier=tier, provider=provider, model="aidraftsman")
|
|
1283
1429
|
|
|
1284
1430
|
usage = data.get("usage") or {}
|
|
@@ -224,12 +224,11 @@ async def run_task(task: dict[str, Any], *, send: Send, thread_id: str = "plan-l
|
|
|
224
224
|
from sdk import local_exec
|
|
225
225
|
from sdk.ipc.handlers import _code_one_repo
|
|
226
226
|
|
|
227
|
-
if not local_exec.
|
|
227
|
+
if not local_exec.can_run_locally():
|
|
228
228
|
await send(Envelope(
|
|
229
229
|
type=Envelope.TYPE_ERROR, thread_id=thread_id,
|
|
230
230
|
data={"code": "CLAUDE_CLI_MISSING",
|
|
231
|
-
"message":
|
|
232
|
-
"Install it and run `claude setup-token`."},
|
|
231
|
+
"message": local_exec.NO_LOCAL_CLI_MESSAGE},
|
|
233
232
|
).model_dump())
|
|
234
233
|
return {"status": "failed", "reason": "claude_cli_missing", "repos": []}
|
|
235
234
|
|
|
@@ -1201,9 +1201,8 @@ def _run_loop(workspace_root: str | None, only_keys: list[str] | None = None,
|
|
|
1201
1201
|
from sdk.client import BackendClient, register_status_observer
|
|
1202
1202
|
from sdk.ipc import handlers
|
|
1203
1203
|
|
|
1204
|
-
if not local_exec.
|
|
1205
|
-
print("error:
|
|
1206
|
-
print(" install it and run `claude setup-token`, then retry.")
|
|
1204
|
+
if not local_exec.can_run_locally():
|
|
1205
|
+
print(f"error: {local_exec.NO_LOCAL_CLI_MESSAGE}")
|
|
1207
1206
|
sys.exit(1)
|
|
1208
1207
|
|
|
1209
1208
|
try:
|
|
@@ -77,6 +77,19 @@ def claude_readiness() -> dict:
|
|
|
77
77
|
"auth_method": None, "email": None, "org": None, "error": str(exc)}
|
|
78
78
|
|
|
79
79
|
|
|
80
|
+
def fallback_readiness() -> dict:
|
|
81
|
+
"""AI Draftsman fallback CLI readiness (installed + logged in) for /status.
|
|
82
|
+
|
|
83
|
+
Reads the stored login only — no server round trip on every /status.
|
|
84
|
+
"""
|
|
85
|
+
try:
|
|
86
|
+
from sdk.local_exec import fallback_status
|
|
87
|
+
return fallback_status()
|
|
88
|
+
except Exception as exc: # noqa: BLE001
|
|
89
|
+
return {"installed": False, "logged_in": False, "verified": None, "path": None,
|
|
90
|
+
"account": None, "base_url": None, "disabled": False, "error": str(exc)}
|
|
91
|
+
|
|
92
|
+
|
|
80
93
|
def environment_info() -> dict:
|
|
81
94
|
"""Env snapshot for the /status endpoint and setup wizard."""
|
|
82
95
|
vp = running_venv()
|
|
@@ -92,6 +105,7 @@ def environment_info() -> dict:
|
|
|
92
105
|
"agentsystem_exists": root.exists(),
|
|
93
106
|
"sdk_version": sdk_version(),
|
|
94
107
|
"claude": claude_readiness(),
|
|
108
|
+
"fallback": fallback_readiness(),
|
|
95
109
|
}
|
|
96
110
|
|
|
97
111
|
|
|
@@ -173,3 +173,129 @@ def test_no_npm_reports_why(tmp_path, monkeypatch, no_claude):
|
|
|
173
173
|
result = asyncio.run(local_exec.run_claude_local("x", str(tmp_path / "p")))
|
|
174
174
|
assert result["ok"] is False
|
|
175
175
|
assert "Node.js 20+" in result["error"]
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
def _fake_auth_cli(tmp_path, *, status, agents_exit=0):
|
|
179
|
+
"""An `aidraftsman` stand-in for the auth commands: records argv, prints `status`."""
|
|
180
|
+
record = tmp_path / "auth.json"
|
|
181
|
+
script = tmp_path / "aidraftsman"
|
|
182
|
+
script.write_text(f"""#!{sys.executable}
|
|
183
|
+
import json, sys
|
|
184
|
+
argv = sys.argv[1:]
|
|
185
|
+
log = json.load(open({str(record)!r})) if __import__("os").path.exists({str(record)!r}) else []
|
|
186
|
+
json.dump(log + [argv], open({str(record)!r}, "w"))
|
|
187
|
+
if argv[:2] == ["auth", "status"]:
|
|
188
|
+
print(json.dumps({status!r}))
|
|
189
|
+
elif argv[:2] == ["agents", "list"]:
|
|
190
|
+
if {agents_exit!r}:
|
|
191
|
+
sys.stderr.write("Request failed (401): Invalid API key")
|
|
192
|
+
sys.exit({agents_exit!r})
|
|
193
|
+
""")
|
|
194
|
+
script.chmod(0o755)
|
|
195
|
+
return script, record
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def test_ad_on_path_is_found(tmp_path, monkeypatch):
|
|
199
|
+
monkeypatch.delenv("AGENTOS_FALLBACK_CLI")
|
|
200
|
+
monkeypatch.setattr(local_exec, "_setting", lambda key: "")
|
|
201
|
+
monkeypatch.setattr(local_exec.shutil, "which",
|
|
202
|
+
lambda name: "/opt/bin/ad" if name == "ad" else None)
|
|
203
|
+
assert local_exec.fallback_cli_path() == "/opt/bin/ad"
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def test_status_reports_stored_login(tmp_path, monkeypatch):
|
|
207
|
+
cli, record = _fake_auth_cli(tmp_path, status={
|
|
208
|
+
"authenticated": True, "baseUrl": "https://dev-api.aidraftsman.ai/",
|
|
209
|
+
"account": "Consultancy Outfit"})
|
|
210
|
+
monkeypatch.setenv("AGENTOS_FALLBACK_CLI", str(cli))
|
|
211
|
+
monkeypatch.delenv("AD_API_KEY", raising=False)
|
|
212
|
+
|
|
213
|
+
st = local_exec.fallback_status()
|
|
214
|
+
assert st["installed"] and st["logged_in"] and st["error"] is None
|
|
215
|
+
assert st["account"] == "Consultancy Outfit"
|
|
216
|
+
assert st["verified"] is None
|
|
217
|
+
assert json.loads(record.read_text()) == [["auth", "status", "--json"]]
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def test_status_not_logged_in_says_how(tmp_path, monkeypatch):
|
|
221
|
+
cli, _ = _fake_auth_cli(tmp_path, status={"authenticated": False})
|
|
222
|
+
monkeypatch.setenv("AGENTOS_FALLBACK_CLI", str(cli))
|
|
223
|
+
monkeypatch.delenv("AD_API_KEY", raising=False)
|
|
224
|
+
|
|
225
|
+
st = local_exec.fallback_status(verify=True)
|
|
226
|
+
assert st["logged_in"] is False
|
|
227
|
+
assert "agentos fallback login" in st["error"]
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def test_status_verify_catches_a_rejected_key(tmp_path, monkeypatch):
|
|
231
|
+
cli, record = _fake_auth_cli(tmp_path, status={"authenticated": True}, agents_exit=2)
|
|
232
|
+
monkeypatch.setenv("AGENTOS_FALLBACK_CLI", str(cli))
|
|
233
|
+
|
|
234
|
+
st = local_exec.fallback_status(verify=True)
|
|
235
|
+
assert st["verified"] is False and st["logged_in"] is False
|
|
236
|
+
assert "401" in st["error"]
|
|
237
|
+
assert json.loads(record.read_text())[-1] == ["agents", "list", "--json"]
|
|
238
|
+
|
|
239
|
+
|
|
240
|
+
def test_status_when_disabled(monkeypatch):
|
|
241
|
+
st = local_exec.fallback_status()
|
|
242
|
+
assert st["disabled"] and not st["installed"]
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
@pytest.mark.parametrize("kwargs, expected", [
|
|
246
|
+
({"method": "browser", "no_browser": True},
|
|
247
|
+
["auth", "login", "--method", "browser", "--no-browser"]),
|
|
248
|
+
({"api_key": "k" * 20, "no_browser": False},
|
|
249
|
+
["auth", "login", "--api-key", "k" * 20, "--method", "api-key"]),
|
|
250
|
+
({"method": "api-key", "base_url": "https://x.example/", "no_browser": False},
|
|
251
|
+
["auth", "login", "--method", "api-key", "--base-url", "https://x.example/"]),
|
|
252
|
+
])
|
|
253
|
+
def test_login_passes_the_method_through(tmp_path, monkeypatch, kwargs, expected):
|
|
254
|
+
cli, record = _fake_auth_cli(tmp_path, status={})
|
|
255
|
+
monkeypatch.setenv("AGENTOS_FALLBACK_CLI", str(cli))
|
|
256
|
+
|
|
257
|
+
assert local_exec.fallback_login(**kwargs) == 0
|
|
258
|
+
assert json.loads(record.read_text()) == [expected]
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
def test_login_on_a_headless_box_does_not_try_a_browser(tmp_path, monkeypatch):
|
|
262
|
+
cli, record = _fake_auth_cli(tmp_path, status={})
|
|
263
|
+
monkeypatch.setenv("AGENTOS_FALLBACK_CLI", str(cli))
|
|
264
|
+
monkeypatch.setenv("SSH_CONNECTION", "1.2.3.4 5 6.7.8.9 22")
|
|
265
|
+
|
|
266
|
+
local_exec.fallback_login(method="browser")
|
|
267
|
+
assert json.loads(record.read_text())[0][-1] == "--no-browser"
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def test_fallback_auth_failure_points_at_login(tmp_path, monkeypatch, no_claude):
|
|
271
|
+
cli, _ = _fake_cli(tmp_path, exit_code=2, stderr="Request failed (401)")
|
|
272
|
+
monkeypatch.setenv("AGENTOS_FALLBACK_CLI", str(cli))
|
|
273
|
+
|
|
274
|
+
result = asyncio.run(local_exec.run_fallback_cli("x", str(tmp_path / "p")))
|
|
275
|
+
assert result["auth_expired"] is True
|
|
276
|
+
assert "agentos fallback login" in result["error"]
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
@pytest.mark.parametrize("claude, fallback, expected", [
|
|
280
|
+
(True, {"installed": False, "logged_in": False}, True),
|
|
281
|
+
(False, {"installed": True, "logged_in": True}, True),
|
|
282
|
+
(False, {"installed": True, "logged_in": False}, False),
|
|
283
|
+
(False, {"installed": False, "logged_in": False}, False),
|
|
284
|
+
])
|
|
285
|
+
def test_can_run_locally(monkeypatch, claude, fallback, expected):
|
|
286
|
+
monkeypatch.setattr(local_exec, "is_available", lambda: claude)
|
|
287
|
+
monkeypatch.setattr(local_exec, "fallback_status", lambda **k: fallback)
|
|
288
|
+
assert local_exec.can_run_locally() is expected
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def test_no_claude_at_all_runs_on_a_logged_in_fallback(tmp_path, monkeypatch, no_claude):
|
|
292
|
+
"""The whole path with claude absent: the gate opens, the spawn falls back."""
|
|
293
|
+
cli, record = _fake_cli(tmp_path, payload={"result": "done", "usage": {}})
|
|
294
|
+
monkeypatch.setenv("AGENTOS_FALLBACK_CLI", str(cli))
|
|
295
|
+
monkeypatch.setattr(local_exec, "fallback_status",
|
|
296
|
+
lambda **k: {"installed": True, "logged_in": True})
|
|
297
|
+
|
|
298
|
+
assert local_exec.can_run_locally() is True
|
|
299
|
+
result = asyncio.run(local_exec.run_claude_local("x", str(tmp_path / "p")))
|
|
300
|
+
assert result["ok"] is True and result["model"] == "aidraftsman:auto"
|
|
301
|
+
assert record.exists()
|
|
@@ -292,6 +292,20 @@ class TestRunGuards:
|
|
|
292
292
|
sent.append(e)
|
|
293
293
|
out = _run(plan_run.run_task({"title": "x", "project_path": "/r"}, send=send))
|
|
294
294
|
assert out["status"] == "failed" and out["reason"] == "claude_cli_missing"
|
|
295
|
+
assert "agentos fallback login" in sent[0]["data"]["message"]
|
|
296
|
+
|
|
297
|
+
def test_no_claude_but_a_logged_in_fallback_gets_past_the_gate(self, monkeypatch):
|
|
298
|
+
monkeypatch.setattr("sdk.local_exec.is_available", lambda: False)
|
|
299
|
+
monkeypatch.setattr("sdk.local_exec.fallback_status",
|
|
300
|
+
lambda **k: {"installed": True, "logged_in": True})
|
|
301
|
+
sent: list[dict] = []
|
|
302
|
+
|
|
303
|
+
async def send(e):
|
|
304
|
+
sent.append(e)
|
|
305
|
+
out = _run(plan_run.run_task({"title": "x", "instructions": "no path here"},
|
|
306
|
+
send=send))
|
|
307
|
+
# Past the CLI gate, so it stops at the NEXT guard instead.
|
|
308
|
+
assert out["reason"] == "no_target"
|
|
295
309
|
|
|
296
310
|
|
|
297
311
|
class TestCli:
|
|
File without changes
|
|
File without changes
|
{devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.57}/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
|