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.
Files changed (228) hide show
  1. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/PKG-INFO +1 -1
  2. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
  3. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/devops_bot_sdk.egg-info/SOURCES.txt +2 -0
  4. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/__init__.py +2 -2
  5. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/cli.py +261 -1
  6. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/client.py +85 -0
  7. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ipc/handlers.py +4 -6
  8. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/local_exec.py +159 -13
  9. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/plan_run.py +2 -3
  10. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/run_auto.py +2 -3
  11. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ticket_fields.py +51 -0
  12. devops_bot_sdk-1.6.60/sdk/tickets.py +177 -0
  13. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/api.py +58 -0
  14. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/models.py +32 -0
  15. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/setup_manager.py +14 -0
  16. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/static/app.js +122 -0
  17. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/static/styles.css +4 -0
  18. devops_bot_sdk-1.6.60/sdk/ui/static/tickets.html +119 -0
  19. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_fallback_cli.py +126 -0
  20. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_task_file_and_plan_run.py +14 -0
  21. devops_bot_sdk-1.6.60/tests/test_ticket_cli.py +342 -0
  22. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_ticket_fields.py +6 -1
  23. devops_bot_sdk-1.6.56/sdk/ui/static/tickets.html +0 -47
  24. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/README.md +0 -0
  25. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
  26. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
  27. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/devops_bot_sdk.egg-info/requires.txt +0 -0
  28. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/devops_bot_sdk.egg-info/top_level.txt +0 -0
  29. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/pyproject.toml +0 -0
  30. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/activity/__init__.py +0 -0
  31. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/activity/classifier.py +0 -0
  32. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/activity/collectors/__init__.py +0 -0
  33. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/activity/collectors/common.py +0 -0
  34. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/activity/collectors/linux_auditd.py +0 -0
  35. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/activity/collectors/macos_eslogger.py +0 -0
  36. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/activity/collectors/windows_eventlog.py +0 -0
  37. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/activity/models.py +0 -0
  38. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/activity/os_setup.py +0 -0
  39. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/activity/recorder.py +0 -0
  40. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/activity/redact.py +0 -0
  41. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/activity/settings.py +0 -0
  42. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/activity/shipper.py +0 -0
  43. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/activity/spool.py +0 -0
  44. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agentd.py +0 -0
  45. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/__init__.py +0 -0
  46. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/activity.py +0 -0
  47. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/builder.py +0 -0
  48. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/chat.py +0 -0
  49. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/connectors.py +0 -0
  50. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/engines/__init__.py +0 -0
  51. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/engines/claude_code.py +0 -0
  52. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/engines/http.py +0 -0
  53. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/engines/mcp.py +0 -0
  54. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/engines/n8n.py +0 -0
  55. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/engines/shell.py +0 -0
  56. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/integrations.py +0 -0
  57. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/inventory.py +0 -0
  58. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/library/__init__.py +0 -0
  59. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/manifest.py +0 -0
  60. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/prompt_studio.py +0 -0
  61. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/prompt_template.py +0 -0
  62. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/prompt_vars.py +0 -0
  63. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/run_log.py +0 -0
  64. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/schedules.py +0 -0
  65. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/secrets.py +0 -0
  66. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/ticket_run.py +0 -0
  67. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/usage.py +0 -0
  68. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/agents/varstore.py +0 -0
  69. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/browser_profile.py +0 -0
  70. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/collectors/__init__.py +0 -0
  71. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/collectors/files.py +0 -0
  72. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/collectors/process.py +0 -0
  73. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/collectors/screenshot.py +0 -0
  74. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/config.py +0 -0
  75. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/crucial.py +0 -0
  76. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/departments.py +0 -0
  77. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/design_verify/__init__.py +0 -0
  78. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/design_verify/assets.py +0 -0
  79. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/design_verify/bootstrap.py +0 -0
  80. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/design_verify/browser.py +0 -0
  81. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/design_verify/compare.py +0 -0
  82. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/design_verify/loop.py +0 -0
  83. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/estimator.py +0 -0
  84. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/exceptions.py +0 -0
  85. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/firewall.py +0 -0
  86. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/formats.py +0 -0
  87. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/git_ops.py +0 -0
  88. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/github_fetch.py +0 -0
  89. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/github_ingest.py +0 -0
  90. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/graph_recon.py +0 -0
  91. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/graphify.py +0 -0
  92. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/hooks/__init__.py +0 -0
  93. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/hooks/crucial_guard.py +0 -0
  94. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/intake.py +0 -0
  95. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ipc/__init__.py +0 -0
  96. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ipc/electron_bridge.py +0 -0
  97. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/journal.py +0 -0
  98. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/memory_guard.py +0 -0
  99. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/models/__init__.py +0 -0
  100. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/models/envelope.py +0 -0
  101. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/models/requests.py +0 -0
  102. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/models/responses.py +0 -0
  103. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/models/snapshots.py +0 -0
  104. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/phase_timer.py +0 -0
  105. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/platform_compat.py +0 -0
  106. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/prd_progress.py +0 -0
  107. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/proof_readers.py +0 -0
  108. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/providers.py +0 -0
  109. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/py.typed +0 -0
  110. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/resume.py +0 -0
  111. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/router.py +0 -0
  112. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/scheduler.py +0 -0
  113. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/self_report.py +0 -0
  114. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/self_report_local.py +0 -0
  115. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/sse.py +0 -0
  116. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/swagger.py +0 -0
  117. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/task_file.py +0 -0
  118. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/task_types.py +0 -0
  119. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/test.py +0 -0
  120. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/test_pipeline.py +0 -0
  121. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/tiers/__init__.py +0 -0
  122. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/tiers/mini_agent.py +0 -0
  123. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/__init__.py +0 -0
  124. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/dashboard.py +0 -0
  125. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/health.py +0 -0
  126. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/ip_allowlist.py +0 -0
  127. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/jobs.py +0 -0
  128. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/runner.py +0 -0
  129. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/security.py +0 -0
  130. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/server.py +0 -0
  131. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/service.py +0 -0
  132. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/session.py +0 -0
  133. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/static/agents.html +0 -0
  134. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/static/chat.html +0 -0
  135. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/static/custom-agents.html +0 -0
  136. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/static/default-agents.html +0 -0
  137. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/static/index.html +0 -0
  138. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/static/report.html +0 -0
  139. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/static/runs.html +0 -0
  140. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/static/schedules.html +0 -0
  141. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/static/setup.html +0 -0
  142. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/ui/swagger.py +0 -0
  143. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/updater.py +0 -0
  144. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/url_access.py +0 -0
  145. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/vendor/aidraftsman-cli-0.1.0.tgz +0 -0
  146. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/vps_health.py +0 -0
  147. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/sdk/write_hook.py +0 -0
  148. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/setup.cfg +0 -0
  149. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_activity_collectors.py +0 -0
  150. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_activity_core.py +0 -0
  151. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_activity_macos.py +0 -0
  152. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_activity_os_setup.py +0 -0
  153. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_activity_pipeline.py +0 -0
  154. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_agent_activity.py +0 -0
  155. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_agent_activity_tracking.py +0 -0
  156. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_agent_chat_features.py +0 -0
  157. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_agent_listing.py +0 -0
  158. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_agent_prompt_async.py +0 -0
  159. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_agent_prompt_models.py +0 -0
  160. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_agent_run_ticket.py +0 -0
  161. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_agent_schedules.py +0 -0
  162. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_agent_workspace_settings.py +0 -0
  163. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_agents_connectors_builder.py +0 -0
  164. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_allowed_tools_config.py +0 -0
  165. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_auto_agent_dispatch.py +0 -0
  166. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_browser_profile.py +0 -0
  167. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_cheap_tier.py +0 -0
  168. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_client_jira_projects.py +0 -0
  169. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_config_token_diagnostics.py +0 -0
  170. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_configure_token_owner.py +0 -0
  171. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_console_encoding.py +0 -0
  172. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_crash_recovery.py +0 -0
  173. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_departments.py +0 -0
  174. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_done_transition.py +0 -0
  175. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_epic_decompose_run.py +0 -0
  176. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_epic_inherited_agent_id.py +0 -0
  177. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_epic_session_isolation.py +0 -0
  178. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_estimator.py +0 -0
  179. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_firewall_open_on_update.py +0 -0
  180. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_formats.py +0 -0
  181. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_git_action.py +0 -0
  182. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_github_fetch.py +0 -0
  183. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_github_ingest.py +0 -0
  184. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_graph_recon.py +0 -0
  185. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_graphify_build.py +0 -0
  186. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_graphify_hook_install.py +0 -0
  187. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_health.py +0 -0
  188. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_intake_job_outcome.py +0 -0
  189. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_intake_plan_review.py +0 -0
  190. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_journal.py +0 -0
  191. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_local_exec_provider.py +0 -0
  192. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_mcp_route_contract.py +0 -0
  193. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_memory_guard.py +0 -0
  194. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_phase_timer.py +0 -0
  195. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_plan_new_command.py +0 -0
  196. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_plan_run_activity.py +0 -0
  197. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_pr_identity.py +0 -0
  198. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_prd_artifact.py +0 -0
  199. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_project_path_resolution.py +0 -0
  200. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_proof_readers.py +0 -0
  201. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_providers.py +0 -0
  202. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_recon_session_isolation.py +0 -0
  203. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_resume.py +0 -0
  204. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_router.py +0 -0
  205. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_run_auto_agent_only.py +0 -0
  206. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_run_outputs.py +0 -0
  207. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_run_verdict_is_honest.py +0 -0
  208. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_schedule_api.py +0 -0
  209. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_schedule_outputs_and_plans.py +0 -0
  210. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_scheduler.py +0 -0
  211. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_self_report.py +0 -0
  212. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_service_token_warning.py +0 -0
  213. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_stale_panel_restart.py +0 -0
  214. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_swagger_command.py +0 -0
  215. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_task_file_format_doc.py +0 -0
  216. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_ticket_run_resolve_by_id.py +0 -0
  217. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_ticket_speed_fixes.py +0 -0
  218. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_tier_flag_parity.py +0 -0
  219. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_ui_gate.py +0 -0
  220. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_ui_lifespan.py +0 -0
  221. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_update_timer.py +0 -0
  222. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_updater_extras.py +0 -0
  223. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_updater_pep668.py +0 -0
  224. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_updater_reports_the_truth.py +0 -0
  225. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_url_access.py +0 -0
  226. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_vps_health.py +0 -0
  227. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_windows_loop_identity.py +0 -0
  228. {devops_bot_sdk-1.6.56 → devops_bot_sdk-1.6.60}/tests/test_write_hook.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devops-bot-sdk
3
- Version: 1.6.56
3
+ Version: 1.6.60
4
4
  Summary: DevOps Bot Desktop SDK — thin client for the AgentOS Electron desktop app
5
5
  Author: noumanaziz2128
6
6
  License-Expression: LicenseRef-Proprietary
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devops-bot-sdk
3
- Version: 1.6.56
3
+ Version: 1.6.60
4
4
  Summary: DevOps Bot Desktop SDK — thin client for the AgentOS Electron desktop app
5
5
  Author: noumanaziz2128
6
6
  License-Expression: LicenseRef-Proprietary
@@ -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.52
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.56" # SINGLE SOURCE OF TRUTH — bump on every change; pyproject,
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.is_available():
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": "The Claude Code CLI is not installed on this machine. "
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.is_available():
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": "The Claude Code CLI is not installed on this machine. "
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