devops-bot-sdk 1.6.56__tar.gz → 1.6.60__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.60}/PKG-INFO +1 -1
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/devops_bot_sdk.egg-info/SOURCES.txt +2 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/__init__.py +2 -2
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/cli.py +261 -1
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/client.py +85 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ipc/handlers.py +4 -6
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/local_exec.py +159 -13
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/plan_run.py +2 -3
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/run_auto.py +2 -3
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ticket_fields.py +51 -0
- devops_bot_sdk-1.6.60/sdk/tickets.py +177 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/api.py +58 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/models.py +32 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/setup_manager.py +14 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/static/app.js +122 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/static/styles.css +4 -0
- devops_bot_sdk-1.6.60/sdk/ui/static/tickets.html +119 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_fallback_cli.py +126 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_task_file_and_plan_run.py +14 -0
- devops_bot_sdk-1.6.60/tests/test_ticket_cli.py +342 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_ticket_fields.py +6 -1
- devops_bot_sdk-1.6.56/sdk/ui/static/tickets.html +0 -47
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/README.md +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/devops_bot_sdk.egg-info/requires.txt +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/devops_bot_sdk.egg-info/top_level.txt +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/pyproject.toml +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/activity/__init__.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/activity/classifier.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/activity/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/activity/collectors/common.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/activity/collectors/linux_auditd.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/activity/collectors/macos_eslogger.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/activity/collectors/windows_eventlog.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/activity/models.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/activity/os_setup.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/activity/recorder.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/activity/redact.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/activity/settings.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/activity/shipper.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/activity/spool.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agentd.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/__init__.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/activity.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/builder.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/chat.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/connectors.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/engines/__init__.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/engines/claude_code.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/engines/http.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/engines/mcp.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/engines/n8n.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/engines/shell.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/integrations.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/inventory.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/library/__init__.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/manifest.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/prompt_studio.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/prompt_template.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/prompt_vars.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/run_log.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/schedules.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/secrets.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/ticket_run.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/usage.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/varstore.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/browser_profile.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/collectors/__init__.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/collectors/files.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/collectors/process.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/collectors/screenshot.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/config.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/crucial.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/departments.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/design_verify/__init__.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/design_verify/assets.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/design_verify/bootstrap.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/design_verify/browser.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/design_verify/compare.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/design_verify/loop.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/estimator.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/exceptions.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/firewall.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/formats.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/git_ops.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/github_fetch.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/github_ingest.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/graph_recon.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/graphify.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/hooks/__init__.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/hooks/crucial_guard.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/intake.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ipc/__init__.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ipc/electron_bridge.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/journal.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/memory_guard.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/models/__init__.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/models/envelope.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/models/requests.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/models/responses.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/models/snapshots.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/phase_timer.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/platform_compat.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/prd_progress.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/proof_readers.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/providers.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/py.typed +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/resume.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/router.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/scheduler.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/self_report.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/self_report_local.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/sse.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/swagger.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/task_file.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/task_types.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/test.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/test_pipeline.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/tiers/__init__.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/tiers/mini_agent.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/__init__.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/dashboard.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/health.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/ip_allowlist.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/jobs.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/runner.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/security.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/server.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/service.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/session.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/static/agents.html +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/static/chat.html +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/static/custom-agents.html +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/static/default-agents.html +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/static/index.html +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/static/report.html +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/static/runs.html +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/static/schedules.html +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/static/setup.html +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/swagger.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/updater.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/url_access.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/vendor/aidraftsman-cli-0.1.0.tgz +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/vps_health.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/write_hook.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/setup.cfg +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_activity_collectors.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_activity_core.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_activity_macos.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_activity_os_setup.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_activity_pipeline.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_agent_activity.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_agent_activity_tracking.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_agent_chat_features.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_agent_listing.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_agent_prompt_async.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_agent_prompt_models.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_agent_run_ticket.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_agent_schedules.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_agent_workspace_settings.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_agents_connectors_builder.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_allowed_tools_config.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_auto_agent_dispatch.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_browser_profile.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_cheap_tier.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_client_jira_projects.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_config_token_diagnostics.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_configure_token_owner.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_console_encoding.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_crash_recovery.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_departments.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_done_transition.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_epic_decompose_run.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_epic_inherited_agent_id.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_epic_session_isolation.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_estimator.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_firewall_open_on_update.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_formats.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_git_action.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_github_fetch.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_github_ingest.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_graph_recon.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_graphify_build.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_graphify_hook_install.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_health.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_intake_job_outcome.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_intake_plan_review.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_journal.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_local_exec_provider.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_mcp_route_contract.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_memory_guard.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_phase_timer.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_plan_new_command.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_plan_run_activity.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_pr_identity.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_prd_artifact.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_project_path_resolution.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_proof_readers.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_providers.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_recon_session_isolation.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_resume.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_router.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_run_auto_agent_only.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_run_outputs.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_run_verdict_is_honest.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_schedule_api.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_schedule_outputs_and_plans.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_scheduler.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_self_report.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_service_token_warning.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_stale_panel_restart.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_swagger_command.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_task_file_format_doc.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_ticket_run_resolve_by_id.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_ticket_speed_fixes.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_tier_flag_parity.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_ui_gate.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_ui_lifespan.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_update_timer.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_updater_extras.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_updater_pep668.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_updater_reports_the_truth.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_url_access.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_vps_health.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_windows_loop_identity.py +0 -0
- {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_write_hook.py +0 -0
|
@@ -47,6 +47,7 @@ sdk/task_types.py
|
|
|
47
47
|
sdk/test.py
|
|
48
48
|
sdk/test_pipeline.py
|
|
49
49
|
sdk/ticket_fields.py
|
|
50
|
+
sdk/tickets.py
|
|
50
51
|
sdk/updater.py
|
|
51
52
|
sdk/url_access.py
|
|
52
53
|
sdk/vps_health.py
|
|
@@ -207,6 +208,7 @@ tests/test_stale_panel_restart.py
|
|
|
207
208
|
tests/test_swagger_command.py
|
|
208
209
|
tests/test_task_file_and_plan_run.py
|
|
209
210
|
tests/test_task_file_format_doc.py
|
|
211
|
+
tests/test_ticket_cli.py
|
|
210
212
|
tests/test_ticket_fields.py
|
|
211
213
|
tests/test_ticket_run_resolve_by_id.py
|
|
212
214
|
tests/test_ticket_speed_fixes.py
|
|
@@ -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.60
|
|
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.60" # 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
|
|
@@ -49,6 +49,21 @@ Command surface:
|
|
|
49
49
|
agentos agent rm <name>
|
|
50
50
|
De-register the agent from the backend and delete its local manifest.
|
|
51
51
|
|
|
52
|
+
agentos ticket create --project KEY --summary TEXT [--type T] [--priority P]
|
|
53
|
+
[--assignee EMAIL] [--description[-file] …] [--instructions[-file] …]
|
|
54
|
+
[--parent ID] [--status S] [--points N]
|
|
55
|
+
File one ticket (POST /tasks-ml) and print its Jira key. Type defaults to
|
|
56
|
+
Task, priority to Medium, assignee to the token owner. Validated locally
|
|
57
|
+
first; a gateway rejection prints the gateway's own messages.
|
|
58
|
+
agentos ticket update KEY_OR_ID [--summary …] [--description[-file] …]
|
|
59
|
+
[--instructions[-file] …] [--priority P] [--status S] [--points N]
|
|
60
|
+
[--assignee EMAIL] [--type T]
|
|
61
|
+
Change only the given fields (PUT /tasks-ml/{id}). Takes a Jira key or
|
|
62
|
+
the platform id. See sdk.tickets — the Tickets page uses the same code.
|
|
63
|
+
agentos ticket delete KEY_OR_ID [--yes]
|
|
64
|
+
Show the ticket and, after a y/N confirmation (skipped by --yes), delete it
|
|
65
|
+
(DELETE /tasks-ml/{id}). Cannot be undone.
|
|
66
|
+
|
|
52
67
|
agentos integration add <path>
|
|
53
68
|
Register an integration (MCP server, etc.) from a JSON file.
|
|
54
69
|
agentos integration test <name>
|
|
@@ -104,6 +119,11 @@ Command surface:
|
|
|
104
119
|
One submission in full — its text, its proofs and the evaluator's
|
|
105
120
|
per-criterion reasoning.
|
|
106
121
|
|
|
122
|
+
agentos fallback install | login [--browser | --api-key [KEY]] | status [--verify] | logout
|
|
123
|
+
The AI Draftsman CLI a ticket re-runs on when `claude` is not working:
|
|
124
|
+
install the bundled copy, log it in (a sign-in code approved on the
|
|
125
|
+
frontend, or an API key), and check it. See sdk.local_exec.fallback_status.
|
|
126
|
+
|
|
107
127
|
agentos agentd start [--foreground]
|
|
108
128
|
Start the local agent daemon (background by default).
|
|
109
129
|
agentos agentd status
|
|
@@ -157,6 +177,10 @@ def main() -> None:
|
|
|
157
177
|
_integration_command(rest)
|
|
158
178
|
return
|
|
159
179
|
|
|
180
|
+
if noun == "ticket":
|
|
181
|
+
_ticket_command(rest)
|
|
182
|
+
return
|
|
183
|
+
|
|
160
184
|
if noun == "agentd":
|
|
161
185
|
_agentd_command(rest)
|
|
162
186
|
return
|
|
@@ -188,23 +212,29 @@ def main() -> None:
|
|
|
188
212
|
_browser_command(rest)
|
|
189
213
|
return
|
|
190
214
|
|
|
215
|
+
if noun == "fallback":
|
|
216
|
+
_fallback_command(rest)
|
|
217
|
+
return
|
|
218
|
+
|
|
191
219
|
_print_usage()
|
|
192
220
|
sys.exit(1)
|
|
193
221
|
|
|
194
222
|
|
|
195
223
|
def _print_usage() -> None:
|
|
196
|
-
print("usage: agentos <configure|chat|agent|integration|default-agents|agentd|github|report|browser|firewall|activity-setup|swagger> ...")
|
|
224
|
+
print("usage: agentos <configure|chat|agent|ticket|integration|default-agents|agentd|github|report|browser|fallback|firewall|activity-setup|swagger> ...")
|
|
197
225
|
print()
|
|
198
226
|
print(" agentos configure [--rotate]")
|
|
199
227
|
print(" agentos chat [--product-id ID] [--pv-id ID] [--no-autorun] # requirements → ticket")
|
|
200
228
|
print(" agentos agent <list|run NAME|chat NAME MSG|logs NAME|enable NAME|disable NAME|rm NAME>")
|
|
201
229
|
print(" agentos agent <set-key NAME KEY[=VALUE]…|keys NAME|rm-key NAME KEY…> # persistent API keys (custom agents)")
|
|
230
|
+
print(" agentos ticket <create|update KEY_OR_ID|delete KEY_OR_ID|help>")
|
|
202
231
|
print(" agentos integration <add PATH|test NAME|list|rm NAME>")
|
|
203
232
|
print(" agentos default-agents list [--department-id UUID] # read-only; agents are created in the web app")
|
|
204
233
|
print(" agentos agentd <start [--foreground]|status>")
|
|
205
234
|
print(" agentos github <ingest [--days N]|status> # PR ingestion; --days N backfills N days")
|
|
206
235
|
print(" agentos report <submit|list|show ID> # report work Jira/GitHub never saw")
|
|
207
236
|
print(" agentos browser <login URL|status|logout> # sign in once; private links become readable")
|
|
237
|
+
print(" agentos fallback <install|login|status|logout> # the AI Draftsman CLI used when claude is not working")
|
|
208
238
|
print(" agentos firewall [--apply] [--revoke IP] [--port N] # open the UI port to the allowlisted IPs")
|
|
209
239
|
print(" agentos firewall --any [--apply] # open the UI port to any source (allowlist is the gate)")
|
|
210
240
|
print(" agentos swagger [--open] [--json [PATH]] # where /docs is, and can you reach it")
|
|
@@ -615,6 +645,153 @@ def _pop_department_id(args: list[str]) -> tuple[str | None, list[str]]:
|
|
|
615
645
|
return dept_id, out
|
|
616
646
|
|
|
617
647
|
|
|
648
|
+
# `agentos ticket` flag → POST/PUT /tasks-ml wire name. Text flags have a
|
|
649
|
+
# `-file` twin that reads the value from a file instead.
|
|
650
|
+
_TICKET_FLAGS = {
|
|
651
|
+
"--project": "projectKey", "--type": "type", "--summary": "summary",
|
|
652
|
+
"--priority": "priority", "--assignee": "assigneeEmail", "--status": "status",
|
|
653
|
+
"--points": "points", "--parent": "parentTaskId",
|
|
654
|
+
"--description": "description", "--instructions": "humanInstructions",
|
|
655
|
+
}
|
|
656
|
+
_TICKET_FILE_FLAGS = {"--description-file": "--description",
|
|
657
|
+
"--instructions-file": "--instructions"}
|
|
658
|
+
# Create-only: neither is editable through PUT /tasks-ml/{id}.
|
|
659
|
+
_TICKET_CREATE_ONLY = {"--project", "--parent"}
|
|
660
|
+
|
|
661
|
+
|
|
662
|
+
def _ticket_usage() -> None:
|
|
663
|
+
print("usage: agentos ticket <create|update KEY_OR_ID|delete KEY_OR_ID|help>")
|
|
664
|
+
print()
|
|
665
|
+
print(" agentos ticket create --project KEY --summary TEXT [--type TYPE] [--priority P]")
|
|
666
|
+
print(" [--assignee EMAIL] [--description TEXT | --description-file PATH]")
|
|
667
|
+
print(" [--instructions TEXT | --instructions-file PATH] [--parent ID] [--status S] [--points N]")
|
|
668
|
+
print(" agentos ticket update KEY_OR_ID [--summary TEXT] [--description TEXT | --description-file PATH]")
|
|
669
|
+
print(" [--instructions TEXT | --instructions-file PATH] [--priority P] [--status S]")
|
|
670
|
+
print(" [--points N] [--assignee EMAIL] [--type TYPE]")
|
|
671
|
+
print(" agentos ticket delete KEY_OR_ID [--yes] # asks to confirm unless --yes")
|
|
672
|
+
print()
|
|
673
|
+
print(" TYPE: Epic|Story|Task|Bug|Sub-task (default Task) P: Lowest|Low|Medium|High|Highest (default Medium)")
|
|
674
|
+
print(" --assignee defaults to the configured token's owner.")
|
|
675
|
+
|
|
676
|
+
|
|
677
|
+
def _parse_ticket_flags(args: list[str], *, creating: bool) -> dict[str, str]:
|
|
678
|
+
"""Flags → wire fields. Exits 1 on an unknown flag, a missing value, both a
|
|
679
|
+
text flag and its -file twin, or an unreadable file."""
|
|
680
|
+
fields: dict[str, str] = {}
|
|
681
|
+
seen: set[str] = set()
|
|
682
|
+
i = 0
|
|
683
|
+
while i < len(args):
|
|
684
|
+
flag = args[i]
|
|
685
|
+
known = flag in _TICKET_FLAGS or flag in _TICKET_FILE_FLAGS
|
|
686
|
+
if not known or (not creating and flag in _TICKET_CREATE_ONLY):
|
|
687
|
+
print(f"error: unknown option for `agentos ticket {'create' if creating else 'update'}`: {flag}")
|
|
688
|
+
sys.exit(1)
|
|
689
|
+
if i + 1 >= len(args):
|
|
690
|
+
print(f"error: expected a value after {flag}")
|
|
691
|
+
sys.exit(1)
|
|
692
|
+
value = args[i + 1]
|
|
693
|
+
text_flag = _TICKET_FILE_FLAGS.get(flag, flag)
|
|
694
|
+
if text_flag in seen:
|
|
695
|
+
print(f"error: pass {text_flag} or {text_flag}-file once, not both")
|
|
696
|
+
sys.exit(1)
|
|
697
|
+
seen.add(text_flag)
|
|
698
|
+
if flag in _TICKET_FILE_FLAGS:
|
|
699
|
+
path = Path(value).expanduser()
|
|
700
|
+
try:
|
|
701
|
+
value = path.read_text(encoding="utf-8")
|
|
702
|
+
except OSError as exc:
|
|
703
|
+
print(f"error: {flag}: cannot read {path}: {exc.strerror or exc}")
|
|
704
|
+
sys.exit(1)
|
|
705
|
+
fields[_TICKET_FLAGS[text_flag]] = value
|
|
706
|
+
i += 2
|
|
707
|
+
return fields
|
|
708
|
+
|
|
709
|
+
|
|
710
|
+
def _ticket_command(args: list[str]) -> None:
|
|
711
|
+
"""`agentos ticket create|update` — see sdk.tickets for the rules."""
|
|
712
|
+
verb = args[0] if args else "help"
|
|
713
|
+
if verb in {"help", "-h", "--help"}:
|
|
714
|
+
_ticket_usage()
|
|
715
|
+
sys.exit(0 if args else 1)
|
|
716
|
+
if verb not in {"create", "update", "delete"}:
|
|
717
|
+
_ticket_usage()
|
|
718
|
+
sys.exit(1)
|
|
719
|
+
if verb in {"update", "delete"} and (len(args) < 2 or args[1].startswith("--")):
|
|
720
|
+
_ticket_usage()
|
|
721
|
+
sys.exit(1)
|
|
722
|
+
|
|
723
|
+
from sdk import tickets
|
|
724
|
+
from sdk.client import BackendClient
|
|
725
|
+
|
|
726
|
+
if verb == "delete":
|
|
727
|
+
_ticket_delete(args[1], args[2:])
|
|
728
|
+
return
|
|
729
|
+
|
|
730
|
+
if verb == "create":
|
|
731
|
+
fields = _parse_ticket_flags(args[1:], creating=True)
|
|
732
|
+
if not fields.get("projectKey") or not fields.get("summary"):
|
|
733
|
+
print("error: --project and --summary are required")
|
|
734
|
+
sys.exit(1)
|
|
735
|
+
else:
|
|
736
|
+
fields = _parse_ticket_flags(args[2:], creating=False)
|
|
737
|
+
if not fields:
|
|
738
|
+
print("error: nothing to update: pass at least one field")
|
|
739
|
+
_ticket_usage()
|
|
740
|
+
sys.exit(1)
|
|
741
|
+
# Validate before building a client, so a bad flag never needs a token.
|
|
742
|
+
err = tickets.validate_fields(fields)
|
|
743
|
+
if err:
|
|
744
|
+
print(f"error: {err}")
|
|
745
|
+
sys.exit(1)
|
|
746
|
+
|
|
747
|
+
client = BackendClient.from_config()
|
|
748
|
+
if verb == "create":
|
|
749
|
+
res = asyncio.run(tickets.create_ticket(client, fields))
|
|
750
|
+
else:
|
|
751
|
+
res = asyncio.run(tickets.update_ticket(client, args[1], fields))
|
|
752
|
+
if not res["ok"]:
|
|
753
|
+
print(f"error: {res['error']}")
|
|
754
|
+
sys.exit(1)
|
|
755
|
+
label = res["key"] or res["id"]
|
|
756
|
+
if verb == "create":
|
|
757
|
+
print(f"created {label} (id {res['id']})")
|
|
758
|
+
else:
|
|
759
|
+
print(f"updated {label}: {', '.join(sorted(fields))}")
|
|
760
|
+
sys.exit(0)
|
|
761
|
+
|
|
762
|
+
|
|
763
|
+
def _ticket_delete(target: str, flags: list[str]) -> None:
|
|
764
|
+
"""`agentos ticket delete KEY_OR_ID [--yes]` — shows the ticket, then asks."""
|
|
765
|
+
from sdk import tickets
|
|
766
|
+
from sdk.client import BackendClient
|
|
767
|
+
|
|
768
|
+
unknown = [f for f in flags if f not in {"--yes", "-y"}]
|
|
769
|
+
if unknown:
|
|
770
|
+
print(f"error: unknown option for `agentos ticket delete`: {unknown[0]}")
|
|
771
|
+
sys.exit(1)
|
|
772
|
+
client = BackendClient.from_config()
|
|
773
|
+
rec = asyncio.run(tickets.get_ticket(client, target))
|
|
774
|
+
if rec is None:
|
|
775
|
+
print(f"error: ticket not found: {target}")
|
|
776
|
+
sys.exit(1)
|
|
777
|
+
label = rec.get("key") or rec.get("id")
|
|
778
|
+
if not flags:
|
|
779
|
+
print(f"{label} [{rec.get('status') or '?'}] {rec.get('summary') or ''}")
|
|
780
|
+
try:
|
|
781
|
+
answer = input(f"Delete {label}? This cannot be undone. [y/N] ")
|
|
782
|
+
except EOFError:
|
|
783
|
+
answer = ""
|
|
784
|
+
if answer.strip().lower() not in {"y", "yes"}:
|
|
785
|
+
print("not deleted")
|
|
786
|
+
sys.exit(1)
|
|
787
|
+
res = asyncio.run(tickets.delete_ticket(client, str(rec.get("id") or target)))
|
|
788
|
+
if not res["ok"]:
|
|
789
|
+
print(f"error: {res['error']}")
|
|
790
|
+
sys.exit(1)
|
|
791
|
+
print(f"deleted {label}")
|
|
792
|
+
sys.exit(0)
|
|
793
|
+
|
|
794
|
+
|
|
618
795
|
def _resolve_department_id(explicit: str | None) -> str:
|
|
619
796
|
"""Use the explicit --department-id, else the caller's own from bootstrap()."""
|
|
620
797
|
if explicit:
|
|
@@ -892,6 +1069,89 @@ def _browser_command(args: list[str]) -> None:
|
|
|
892
1069
|
sys.exit(1)
|
|
893
1070
|
|
|
894
1071
|
|
|
1072
|
+
# ── fallback ─────────────────────────────────────────────────────────────
|
|
1073
|
+
|
|
1074
|
+
def _fallback_usage() -> None:
|
|
1075
|
+
print("usage: agentos fallback install # npm-install the bundled CLI now")
|
|
1076
|
+
print(" agentos fallback login [--browser | --api-key [KEY]] [--base-url URL] [--no-browser]")
|
|
1077
|
+
print(" agentos fallback status [--verify] # --verify asks the server too")
|
|
1078
|
+
print(" agentos fallback logout")
|
|
1079
|
+
print()
|
|
1080
|
+
print(" When the `claude` CLI is not working, tickets re-run on the AI Draftsman CLI")
|
|
1081
|
+
print(" bundled with the SDK. It needs its own login, made as the user the SDK runs as.")
|
|
1082
|
+
print()
|
|
1083
|
+
print(" --browser prints a sign-in code and a link; approve it on the AI Draftsman")
|
|
1084
|
+
print(" frontend from any machine — works on a headless VPS")
|
|
1085
|
+
print(" --api-key paste a key from Settings → API keys (hidden prompt), or pass")
|
|
1086
|
+
print(" KEY after the flag")
|
|
1087
|
+
print(" (neither) the CLI's own menu")
|
|
1088
|
+
|
|
1089
|
+
|
|
1090
|
+
def _fallback_command(args: list[str]) -> None:
|
|
1091
|
+
"""``agentos fallback install|login|status|logout`` — the AI Draftsman fallback CLI."""
|
|
1092
|
+
from sdk import local_exec as le
|
|
1093
|
+
|
|
1094
|
+
verb = args[0] if args else None
|
|
1095
|
+
rest = args[1:]
|
|
1096
|
+
|
|
1097
|
+
if verb == "install":
|
|
1098
|
+
if le._fallback_disabled():
|
|
1099
|
+
print("error: the fallback is disabled (AGENTOS_FALLBACK_CLI=off)")
|
|
1100
|
+
sys.exit(1)
|
|
1101
|
+
cli = asyncio.run(le.ensure_fallback_cli())
|
|
1102
|
+
if not cli:
|
|
1103
|
+
print("error: could not install the AI Draftsman CLI (Node.js 20+ and npm are needed)")
|
|
1104
|
+
sys.exit(1)
|
|
1105
|
+
print(f"installed: {cli}")
|
|
1106
|
+
ad = Path(cli).with_name("ad.cmd" if cli.lower().endswith(".cmd") else "ad")
|
|
1107
|
+
if ad.exists():
|
|
1108
|
+
print(f"also as : {ad} (the same CLI under its short name)")
|
|
1109
|
+
print("next : agentos fallback login")
|
|
1110
|
+
return
|
|
1111
|
+
|
|
1112
|
+
if verb == "login":
|
|
1113
|
+
method = api_key = base_url = None
|
|
1114
|
+
no_browser = True if "--no-browser" in rest else None
|
|
1115
|
+
if "--browser" in rest:
|
|
1116
|
+
method = "browser"
|
|
1117
|
+
if "--api-key" in rest:
|
|
1118
|
+
method = "api-key"
|
|
1119
|
+
i = rest.index("--api-key") + 1
|
|
1120
|
+
if i < len(rest) and not rest[i].startswith("-"):
|
|
1121
|
+
api_key = rest[i]
|
|
1122
|
+
if "--base-url" in rest:
|
|
1123
|
+
try:
|
|
1124
|
+
base_url = rest[rest.index("--base-url") + 1]
|
|
1125
|
+
except IndexError:
|
|
1126
|
+
print("error: --base-url needs a URL")
|
|
1127
|
+
sys.exit(1)
|
|
1128
|
+
rc = le.fallback_login(method=method, api_key=api_key, base_url=base_url,
|
|
1129
|
+
no_browser=no_browser)
|
|
1130
|
+
if rc == 0:
|
|
1131
|
+
print("check it : agentos fallback status --verify")
|
|
1132
|
+
sys.exit(rc)
|
|
1133
|
+
|
|
1134
|
+
if verb == "status":
|
|
1135
|
+
st = le.fallback_status(verify="--verify" in rest)
|
|
1136
|
+
print(f"installed : {'yes' if st['installed'] else 'no'}")
|
|
1137
|
+
print(f"path : {st['path'] or '-'}")
|
|
1138
|
+
print(f"logged in : {'yes' if st['logged_in'] else 'no'}")
|
|
1139
|
+
if st["verified"] is not None:
|
|
1140
|
+
print(f"server : {'key accepted' if st['verified'] else 'NOT accepted'}")
|
|
1141
|
+
print(f"account : {st['account'] or '-'}")
|
|
1142
|
+
print(f"server url: {st['base_url'] or '-'}")
|
|
1143
|
+
if st["error"]:
|
|
1144
|
+
print(f"problem : {st['error']}")
|
|
1145
|
+
ready = st["logged_in"] and st["verified"] is not False
|
|
1146
|
+
sys.exit(0 if ready else 1)
|
|
1147
|
+
|
|
1148
|
+
if verb == "logout":
|
|
1149
|
+
sys.exit(le.fallback_logout())
|
|
1150
|
+
|
|
1151
|
+
_fallback_usage()
|
|
1152
|
+
sys.exit(1)
|
|
1153
|
+
|
|
1154
|
+
|
|
895
1155
|
def _report_check(url: str) -> None:
|
|
896
1156
|
"""``agentos report check <URL>`` — can this machine open that link?
|
|
897
1157
|
|
|
@@ -249,6 +249,7 @@ class BackendClient:
|
|
|
249
249
|
# busy backend (retry in 30 min) from a rejected request (wait for the
|
|
250
250
|
# next slot). Empty dict means "no failure recorded".
|
|
251
251
|
self.last_ingest_error: dict = {}
|
|
252
|
+
self.last_task_ml_error: dict = {}
|
|
252
253
|
self._headers = {
|
|
253
254
|
"Authorization": token, # co_<uuid>_<hex> — no Bearer prefix
|
|
254
255
|
"X-SDK-Version": SDK_VERSION,
|
|
@@ -1274,6 +1275,48 @@ class BackendClient:
|
|
|
1274
1275
|
logger.warning("client.get_task_status list failed task=%s err=%s", task_id, exc)
|
|
1275
1276
|
return None
|
|
1276
1277
|
|
|
1278
|
+
async def get_task_ml(self, task_id: str) -> dict | None:
|
|
1279
|
+
"""GET {BE}/tasks-ml/{id} → the full RAW record (jiraIssueKey, summary, …), or None.
|
|
1280
|
+
|
|
1281
|
+
Same read as ``get_task_status`` — direct single-record read, then a scan
|
|
1282
|
+
of the list endpoint for a backend that doesn't serve it — but returns the
|
|
1283
|
+
whole record. Matches `id` or the Jira key. Best-effort: never raises.
|
|
1284
|
+
"""
|
|
1285
|
+
from sdk.config import BE_DEFAULT_URL
|
|
1286
|
+
|
|
1287
|
+
if not task_id:
|
|
1288
|
+
return None
|
|
1289
|
+
base = BE_DEFAULT_URL.rstrip("/")
|
|
1290
|
+
headers = {**self._headers, "accept": "*/*", "developer-name": "null", "product": ""}
|
|
1291
|
+
try:
|
|
1292
|
+
async with httpx.AsyncClient(timeout=20.0) as client:
|
|
1293
|
+
resp = await client.get(f"{base}/tasks-ml/{task_id}", headers=headers)
|
|
1294
|
+
if resp.status_code < 400:
|
|
1295
|
+
data = (resp.json() or {}).get("data") or {}
|
|
1296
|
+
if isinstance(data, dict):
|
|
1297
|
+
rec = data.get("record") if isinstance(data.get("record"), dict) else data
|
|
1298
|
+
if rec.get("id"):
|
|
1299
|
+
return rec
|
|
1300
|
+
except Exception as exc: # noqa: BLE001 — fall through to the list scan
|
|
1301
|
+
logger.warning("client.get_task_ml direct failed task=%s err=%s", task_id, exc)
|
|
1302
|
+
try:
|
|
1303
|
+
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
1304
|
+
resp = await client.get(f"{base}/tasks-ml", params={"limit": 10000},
|
|
1305
|
+
headers=headers)
|
|
1306
|
+
if resp.status_code >= 400:
|
|
1307
|
+
return None
|
|
1308
|
+
records = ((resp.json() or {}).get("data") or {}).get("records") or []
|
|
1309
|
+
want = str(task_id).strip().upper()
|
|
1310
|
+
for rec in records:
|
|
1311
|
+
if isinstance(rec, dict) and want in {
|
|
1312
|
+
str(rec.get(k) or "").strip().upper()
|
|
1313
|
+
for k in ("id", "jiraIssueKey", "jiraKey")
|
|
1314
|
+
}:
|
|
1315
|
+
return rec
|
|
1316
|
+
except Exception as exc: # noqa: BLE001
|
|
1317
|
+
logger.warning("client.get_task_ml list failed task=%s err=%s", task_id, exc)
|
|
1318
|
+
return None
|
|
1319
|
+
|
|
1277
1320
|
async def set_task_status(self, task_id: str, status: str) -> bool:
|
|
1278
1321
|
"""PUT {BE}/tasks-ml/{id} status=<status> (best-effort, arbitrary target).
|
|
1279
1322
|
|
|
@@ -1435,11 +1478,17 @@ class BackendClient:
|
|
|
1435
1478
|
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
1436
1479
|
resp = await client.put(url, files=files, headers=headers)
|
|
1437
1480
|
if resp.status_code >= 400:
|
|
1481
|
+
try:
|
|
1482
|
+
self.last_task_ml_error = resp.json() or {}
|
|
1483
|
+
except Exception: # noqa: BLE001
|
|
1484
|
+
self.last_task_ml_error = {"error": resp.text or "request failed"}
|
|
1438
1485
|
logger.warning("client.update_task_ml failed task=%s status=%s body=%s",
|
|
1439
1486
|
task_id, resp.status_code, resp.text[:200])
|
|
1440
1487
|
return False
|
|
1488
|
+
self.last_task_ml_error = {}
|
|
1441
1489
|
return True
|
|
1442
1490
|
except Exception as exc: # noqa: BLE001
|
|
1491
|
+
self.last_task_ml_error = {"error": str(exc)}
|
|
1443
1492
|
logger.warning("client.update_task_ml error task=%s err=%s", task_id, exc)
|
|
1444
1493
|
return False
|
|
1445
1494
|
|
|
@@ -1458,6 +1507,7 @@ class BackendClient:
|
|
|
1458
1507
|
from sdk.config import BE_DEFAULT_URL
|
|
1459
1508
|
from sdk.ticket_fields import to_multipart
|
|
1460
1509
|
|
|
1510
|
+
self.last_task_ml_error = {}
|
|
1461
1511
|
url = f"{BE_DEFAULT_URL.rstrip('/')}/tasks-ml"
|
|
1462
1512
|
headers = {k: v for k, v in self._headers.items() if k.lower() != "content-type"}
|
|
1463
1513
|
headers.update({"accept": "*/*", "developer-name": "null", "product": ""})
|
|
@@ -1468,9 +1518,14 @@ class BackendClient:
|
|
|
1468
1518
|
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
1469
1519
|
resp = await client.post(url, files=files, headers=headers)
|
|
1470
1520
|
if resp.status_code >= 400:
|
|
1521
|
+
try:
|
|
1522
|
+
self.last_task_ml_error = resp.json() or {}
|
|
1523
|
+
except Exception: # noqa: BLE001
|
|
1524
|
+
self.last_task_ml_error = {"error": resp.text or "request failed"}
|
|
1471
1525
|
logger.warning("client.create_task_ml failed status=%s body=%s",
|
|
1472
1526
|
resp.status_code, resp.text[:300])
|
|
1473
1527
|
return None
|
|
1528
|
+
self.last_task_ml_error = {}
|
|
1474
1529
|
body = resp.json() or {}
|
|
1475
1530
|
rec = body.get("data") or body
|
|
1476
1531
|
# The created record's id may sit at data.id or data.record.id.
|
|
@@ -1478,9 +1533,39 @@ class BackendClient:
|
|
|
1478
1533
|
return rec.get("id") or ((rec.get("record") or {}).get("id"))
|
|
1479
1534
|
return None
|
|
1480
1535
|
except Exception as exc: # noqa: BLE001
|
|
1536
|
+
self.last_task_ml_error = {"error": str(exc)}
|
|
1481
1537
|
logger.warning("client.create_task_ml error err=%s", exc)
|
|
1482
1538
|
return None
|
|
1483
1539
|
|
|
1540
|
+
async def delete_task_ml(self, task_id: str) -> bool:
|
|
1541
|
+
"""DELETE {BE}/tasks-ml/{id} — remove a ticket. True on 2xx.
|
|
1542
|
+
|
|
1543
|
+
On failure the gateway's JSON body (e.g. ``{"message": "Task not found"}``)
|
|
1544
|
+
is kept in ``last_task_ml_error``, as for create/update. Never raises.
|
|
1545
|
+
"""
|
|
1546
|
+
from sdk.config import BE_DEFAULT_URL
|
|
1547
|
+
|
|
1548
|
+
url = f"{BE_DEFAULT_URL.rstrip('/')}/tasks-ml/{task_id}"
|
|
1549
|
+
headers = {k: v for k, v in self._headers.items() if k.lower() != "content-type"}
|
|
1550
|
+
headers.update({"accept": "*/*", "developer-name": "null", "product": ""})
|
|
1551
|
+
try:
|
|
1552
|
+
async with httpx.AsyncClient(timeout=30.0) as client:
|
|
1553
|
+
resp = await client.delete(url, headers=headers)
|
|
1554
|
+
if resp.status_code >= 400:
|
|
1555
|
+
try:
|
|
1556
|
+
self.last_task_ml_error = resp.json() or {}
|
|
1557
|
+
except Exception: # noqa: BLE001
|
|
1558
|
+
self.last_task_ml_error = {"error": resp.text or "request failed"}
|
|
1559
|
+
logger.warning("client.delete_task_ml failed task=%s status=%s body=%s",
|
|
1560
|
+
task_id, resp.status_code, resp.text[:200])
|
|
1561
|
+
return False
|
|
1562
|
+
self.last_task_ml_error = {}
|
|
1563
|
+
return True
|
|
1564
|
+
except Exception as exc: # noqa: BLE001
|
|
1565
|
+
self.last_task_ml_error = {"error": str(exc)}
|
|
1566
|
+
logger.warning("client.delete_task_ml error task=%s err=%s", task_id, exc)
|
|
1567
|
+
return False
|
|
1568
|
+
|
|
1484
1569
|
# ── Jira projects ──────────────────────────────────────────────────
|
|
1485
1570
|
# A Jira project is the container a ticket's `projectKey` points at, so these
|
|
1486
1571
|
# sit next to create_task_ml: a flow that has no projectKey yet resolves one
|
|
@@ -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
|
|