openhands-automation 1.0.0__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 (309) hide show
  1. openhands_automation-1.0.0/.dockerignore +32 -0
  2. openhands_automation-1.0.0/.github/workflows/ci.yml +56 -0
  3. openhands_automation-1.0.0/.github/workflows/ghcr-build.yml +102 -0
  4. openhands_automation-1.0.0/.github/workflows/pr-artifacts.yml +142 -0
  5. openhands_automation-1.0.0/.github/workflows/prepare-release.yml +135 -0
  6. openhands_automation-1.0.0/.github/workflows/promote-to-development.yml +90 -0
  7. openhands_automation-1.0.0/.github/workflows/pypi-release.yml +40 -0
  8. openhands_automation-1.0.0/.github/workflows/qa-changes-by-openhands.yml +41 -0
  9. openhands_automation-1.0.0/.github/workflows/tag-image.yml +70 -0
  10. openhands_automation-1.0.0/.github/workflows/tests.yml +49 -0
  11. openhands_automation-1.0.0/.gitignore +193 -0
  12. openhands_automation-1.0.0/.pre-commit-config.yaml +45 -0
  13. openhands_automation-1.0.0/.python-version +1 -0
  14. openhands_automation-1.0.0/AGENTS.md +304 -0
  15. openhands_automation-1.0.0/LICENSE +21 -0
  16. openhands_automation-1.0.0/Makefile +96 -0
  17. openhands_automation-1.0.0/PKG-INFO +131 -0
  18. openhands_automation-1.0.0/README.md +97 -0
  19. openhands_automation-1.0.0/alembic.ini +37 -0
  20. openhands_automation-1.0.0/containers/Dockerfile +45 -0
  21. openhands_automation-1.0.0/docs/kv-store-client-guide.md +451 -0
  22. openhands_automation-1.0.0/docs/kv-store-design.md +910 -0
  23. openhands_automation-1.0.0/docs/kv-store-test-plan.md +147 -0
  24. openhands_automation-1.0.0/frontend/.env.example +3 -0
  25. openhands_automation-1.0.0/frontend/.eslintrc +73 -0
  26. openhands_automation-1.0.0/frontend/.gitignore +10 -0
  27. openhands_automation-1.0.0/frontend/.husky/pre-commit +2 -0
  28. openhands_automation-1.0.0/frontend/.prettierrc.json +3 -0
  29. openhands_automation-1.0.0/frontend/README.md +234 -0
  30. openhands_automation-1.0.0/frontend/__mocks__/zustand.ts +60 -0
  31. openhands_automation-1.0.0/frontend/commitlint.config.cjs +3 -0
  32. openhands_automation-1.0.0/frontend/package-lock.json +12444 -0
  33. openhands_automation-1.0.0/frontend/package.json +109 -0
  34. openhands_automation-1.0.0/frontend/playwright.config.ts +25 -0
  35. openhands_automation-1.0.0/frontend/public/mockServiceWorker.js +349 -0
  36. openhands_automation-1.0.0/frontend/react-router.config.ts +28 -0
  37. openhands_automation-1.0.0/frontend/scripts/check-translation-completeness.cjs +102 -0
  38. openhands_automation-1.0.0/frontend/scripts/dev-proxy.mjs +101 -0
  39. openhands_automation-1.0.0/frontend/scripts/make-i18n-translations.cjs +51 -0
  40. openhands_automation-1.0.0/frontend/src/__tests__/api/automation-service.test.ts +180 -0
  41. openhands_automation-1.0.0/frontend/src/__tests__/api/openhands-service.test.ts +61 -0
  42. openhands_automation-1.0.0/frontend/src/__tests__/components/automations/automation-card.test.tsx +136 -0
  43. openhands_automation-1.0.0/frontend/src/__tests__/components/automations/automation-group.test.tsx +53 -0
  44. openhands_automation-1.0.0/frontend/src/__tests__/components/automations/create-instructions.test.tsx +123 -0
  45. openhands_automation-1.0.0/frontend/src/__tests__/components/automations/delete-confirmation-modal.test.tsx +71 -0
  46. openhands_automation-1.0.0/frontend/src/__tests__/components/automations/detail/active-status-badge.test.tsx +15 -0
  47. openhands_automation-1.0.0/frontend/src/__tests__/components/automations/detail/activity-log-item.test.tsx +240 -0
  48. openhands_automation-1.0.0/frontend/src/__tests__/components/automations/detail/activity-log-section.test.tsx +98 -0
  49. openhands_automation-1.0.0/frontend/src/__tests__/components/automations/detail/activity-section.test.tsx +79 -0
  50. openhands_automation-1.0.0/frontend/src/__tests__/components/automations/detail/back-link.test.tsx +29 -0
  51. openhands_automation-1.0.0/frontend/src/__tests__/components/automations/detail/branch-badge.test.tsx +15 -0
  52. openhands_automation-1.0.0/frontend/src/__tests__/components/automations/detail/config-field.test.tsx +29 -0
  53. openhands_automation-1.0.0/frontend/src/__tests__/components/automations/detail/configuration-section.test.tsx +74 -0
  54. openhands_automation-1.0.0/frontend/src/__tests__/components/automations/detail/detail-header.test.tsx +119 -0
  55. openhands_automation-1.0.0/frontend/src/__tests__/components/automations/detail/not-found-state.test.tsx +24 -0
  56. openhands_automation-1.0.0/frontend/src/__tests__/components/automations/detail/plugin-chip.test.tsx +15 -0
  57. openhands_automation-1.0.0/frontend/src/__tests__/components/automations/detail/plugins-section.test.tsx +21 -0
  58. openhands_automation-1.0.0/frontend/src/__tests__/components/automations/detail/prompt-section.test.tsx +21 -0
  59. openhands_automation-1.0.0/frontend/src/__tests__/components/automations/detail/run-status-badge.test.tsx +35 -0
  60. openhands_automation-1.0.0/frontend/src/__tests__/components/automations/detail/section-card.test.tsx +17 -0
  61. openhands_automation-1.0.0/frontend/src/__tests__/components/automations/empty-state.test.tsx +66 -0
  62. openhands_automation-1.0.0/frontend/src/__tests__/components/automations/error-state.test.tsx +23 -0
  63. openhands_automation-1.0.0/frontend/src/__tests__/components/automations/kebab-menu.test.tsx +63 -0
  64. openhands_automation-1.0.0/frontend/src/__tests__/components/automations/metadata-chip.test.tsx +17 -0
  65. openhands_automation-1.0.0/frontend/src/__tests__/components/automations/permission-gating.test.tsx +141 -0
  66. openhands_automation-1.0.0/frontend/src/__tests__/components/automations/search-input.test.tsx +28 -0
  67. openhands_automation-1.0.0/frontend/src/__tests__/components/automations/toggle-switch.test.tsx +38 -0
  68. openhands_automation-1.0.0/frontend/src/__tests__/components/reauth-modal.test.tsx +12 -0
  69. openhands_automation-1.0.0/frontend/src/__tests__/components/shared/modals/modal-backdrop.test.tsx +76 -0
  70. openhands_automation-1.0.0/frontend/src/__tests__/components/shared/modals/modal-body.test.tsx +61 -0
  71. openhands_automation-1.0.0/frontend/src/__tests__/hooks/use-auto-login.test.ts +102 -0
  72. openhands_automation-1.0.0/frontend/src/__tests__/hooks/use-cross-tab-state.test.ts +54 -0
  73. openhands_automation-1.0.0/frontend/src/__tests__/hooks/use-escape-key.test.ts +53 -0
  74. openhands_automation-1.0.0/frontend/src/__tests__/hooks/use-has-permission.test.ts +83 -0
  75. openhands_automation-1.0.0/frontend/src/__tests__/hooks/use-is-authed.test.ts +75 -0
  76. openhands_automation-1.0.0/frontend/src/__tests__/hooks/use-language-sync.test.ts +142 -0
  77. openhands_automation-1.0.0/frontend/src/__tests__/hooks/use-me.test.ts +61 -0
  78. openhands_automation-1.0.0/frontend/src/__tests__/hooks/use-org-sync.test.ts +129 -0
  79. openhands_automation-1.0.0/frontend/src/__tests__/hooks/use-theme-sync.test.ts +114 -0
  80. openhands_automation-1.0.0/frontend/src/__tests__/routes/automation-detail.test.tsx +277 -0
  81. openhands_automation-1.0.0/frontend/src/__tests__/routes/automations-list.test.tsx +242 -0
  82. openhands_automation-1.0.0/frontend/src/__tests__/routes/root-layout.test.tsx +177 -0
  83. openhands_automation-1.0.0/frontend/src/__tests__/stores/user-store.test.ts +42 -0
  84. openhands_automation-1.0.0/frontend/src/__tests__/utils/generate-auth-url.test.ts +88 -0
  85. openhands_automation-1.0.0/frontend/src/__tests__/utils/local-storage.test.ts +60 -0
  86. openhands_automation-1.0.0/frontend/src/api/automation-service.ts +83 -0
  87. openhands_automation-1.0.0/frontend/src/api/axios-clients.ts +21 -0
  88. openhands_automation-1.0.0/frontend/src/api/openhands-service.ts +16 -0
  89. openhands_automation-1.0.0/frontend/src/assets/branding/openhands-logo.svg +9 -0
  90. openhands_automation-1.0.0/frontend/src/components/.gitkeep +0 -0
  91. openhands_automation-1.0.0/frontend/src/components/automations/automation-card-skeleton.tsx +19 -0
  92. openhands_automation-1.0.0/frontend/src/components/automations/automation-card.tsx +119 -0
  93. openhands_automation-1.0.0/frontend/src/components/automations/automation-group.tsx +40 -0
  94. openhands_automation-1.0.0/frontend/src/components/automations/create-instructions.tsx +119 -0
  95. openhands_automation-1.0.0/frontend/src/components/automations/delete-confirmation-modal.tsx +70 -0
  96. openhands_automation-1.0.0/frontend/src/components/automations/detail/active-status-badge.tsx +24 -0
  97. openhands_automation-1.0.0/frontend/src/components/automations/detail/activity-log-item.tsx +96 -0
  98. openhands_automation-1.0.0/frontend/src/components/automations/detail/activity-log-section.tsx +82 -0
  99. openhands_automation-1.0.0/frontend/src/components/automations/detail/activity-section.tsx +77 -0
  100. openhands_automation-1.0.0/frontend/src/components/automations/detail/back-link.tsx +18 -0
  101. openhands_automation-1.0.0/frontend/src/components/automations/detail/branch-badge.tsx +11 -0
  102. openhands_automation-1.0.0/frontend/src/components/automations/detail/config-field.tsx +17 -0
  103. openhands_automation-1.0.0/frontend/src/components/automations/detail/configuration-section.tsx +84 -0
  104. openhands_automation-1.0.0/frontend/src/components/automations/detail/detail-header.tsx +84 -0
  105. openhands_automation-1.0.0/frontend/src/components/automations/detail/detail-skeleton.tsx +28 -0
  106. openhands_automation-1.0.0/frontend/src/components/automations/detail/not-found-state.tsx +23 -0
  107. openhands_automation-1.0.0/frontend/src/components/automations/detail/plugin-chip.tsx +11 -0
  108. openhands_automation-1.0.0/frontend/src/components/automations/detail/plugins-section.tsx +26 -0
  109. openhands_automation-1.0.0/frontend/src/components/automations/detail/prompt-section.tsx +25 -0
  110. openhands_automation-1.0.0/frontend/src/components/automations/detail/run-status-badge.tsx +62 -0
  111. openhands_automation-1.0.0/frontend/src/components/automations/detail/section-card.tsx +17 -0
  112. openhands_automation-1.0.0/frontend/src/components/automations/empty-state.tsx +22 -0
  113. openhands_automation-1.0.0/frontend/src/components/automations/error-state.tsx +27 -0
  114. openhands_automation-1.0.0/frontend/src/components/automations/kebab-menu.tsx +69 -0
  115. openhands_automation-1.0.0/frontend/src/components/automations/metadata-chip.tsx +13 -0
  116. openhands_automation-1.0.0/frontend/src/components/automations/search-input.tsx +25 -0
  117. openhands_automation-1.0.0/frontend/src/components/automations/status-badge.tsx +11 -0
  118. openhands_automation-1.0.0/frontend/src/components/automations/toggle-switch.tsx +33 -0
  119. openhands_automation-1.0.0/frontend/src/components/error-boundary.tsx +48 -0
  120. openhands_automation-1.0.0/frontend/src/components/reauth-modal.tsx +22 -0
  121. openhands_automation-1.0.0/frontend/src/components/shared/modals/modal-backdrop.tsx +35 -0
  122. openhands_automation-1.0.0/frontend/src/components/shared/modals/modal-body.tsx +32 -0
  123. openhands_automation-1.0.0/frontend/src/constants/.gitkeep +0 -0
  124. openhands_automation-1.0.0/frontend/src/entry.client.tsx +37 -0
  125. openhands_automation-1.0.0/frontend/src/hooks/.gitkeep +0 -0
  126. openhands_automation-1.0.0/frontend/src/hooks/use-auth-url.ts +8 -0
  127. openhands_automation-1.0.0/frontend/src/hooks/use-auto-login.ts +59 -0
  128. openhands_automation-1.0.0/frontend/src/hooks/use-automation-detail.ts +23 -0
  129. openhands_automation-1.0.0/frontend/src/hooks/use-automations.ts +35 -0
  130. openhands_automation-1.0.0/frontend/src/hooks/use-cross-tab-state.ts +48 -0
  131. openhands_automation-1.0.0/frontend/src/hooks/use-escape-key.ts +26 -0
  132. openhands_automation-1.0.0/frontend/src/hooks/use-has-permission.ts +9 -0
  133. openhands_automation-1.0.0/frontend/src/hooks/use-is-authed.ts +25 -0
  134. openhands_automation-1.0.0/frontend/src/hooks/use-language-sync.ts +54 -0
  135. openhands_automation-1.0.0/frontend/src/hooks/use-me.ts +18 -0
  136. openhands_automation-1.0.0/frontend/src/hooks/use-org-sync.ts +40 -0
  137. openhands_automation-1.0.0/frontend/src/hooks/use-theme-sync.ts +39 -0
  138. openhands_automation-1.0.0/frontend/src/i18n/index.ts +38 -0
  139. openhands_automation-1.0.0/frontend/src/i18n/translation.json +1056 -0
  140. openhands_automation-1.0.0/frontend/src/icons/.gitkeep +0 -0
  141. openhands_automation-1.0.0/frontend/src/icons/activity.svg +3 -0
  142. openhands_automation-1.0.0/frontend/src/icons/bell.svg +3 -0
  143. openhands_automation-1.0.0/frontend/src/icons/calendar.svg +3 -0
  144. openhands_automation-1.0.0/frontend/src/icons/check-circle.svg +3 -0
  145. openhands_automation-1.0.0/frontend/src/icons/chevron-down.svg +3 -0
  146. openhands_automation-1.0.0/frontend/src/icons/chevron-left.svg +3 -0
  147. openhands_automation-1.0.0/frontend/src/icons/clock.svg +3 -0
  148. openhands_automation-1.0.0/frontend/src/icons/cog.svg +4 -0
  149. openhands_automation-1.0.0/frontend/src/icons/database.svg +3 -0
  150. openhands_automation-1.0.0/frontend/src/icons/download.svg +3 -0
  151. openhands_automation-1.0.0/frontend/src/icons/exclamation-circle.svg +3 -0
  152. openhands_automation-1.0.0/frontend/src/icons/folder.svg +3 -0
  153. openhands_automation-1.0.0/frontend/src/icons/git-branch.svg +6 -0
  154. openhands_automation-1.0.0/frontend/src/icons/kebab-vertical.svg +3 -0
  155. openhands_automation-1.0.0/frontend/src/icons/power.svg +3 -0
  156. openhands_automation-1.0.0/frontend/src/icons/puzzle.svg +3 -0
  157. openhands_automation-1.0.0/frontend/src/icons/search.svg +3 -0
  158. openhands_automation-1.0.0/frontend/src/icons/sparkle.svg +3 -0
  159. openhands_automation-1.0.0/frontend/src/icons/svg.d.ts +6 -0
  160. openhands_automation-1.0.0/frontend/src/icons/target.svg +3 -0
  161. openhands_automation-1.0.0/frontend/src/icons/terminal.svg +3 -0
  162. openhands_automation-1.0.0/frontend/src/icons/trash.svg +3 -0
  163. openhands_automation-1.0.0/frontend/src/icons/x-circle.svg +3 -0
  164. openhands_automation-1.0.0/frontend/src/icons/x-mark.svg +3 -0
  165. openhands_automation-1.0.0/frontend/src/index.css +40 -0
  166. openhands_automation-1.0.0/frontend/src/mocks/auth-handlers.ts +41 -0
  167. openhands_automation-1.0.0/frontend/src/mocks/automation-handlers.ts +123 -0
  168. openhands_automation-1.0.0/frontend/src/mocks/automation-runs.mock.ts +61 -0
  169. openhands_automation-1.0.0/frontend/src/mocks/automations.mock.ts +127 -0
  170. openhands_automation-1.0.0/frontend/src/mocks/browser.ts +4 -0
  171. openhands_automation-1.0.0/frontend/src/mocks/handlers.ts +4 -0
  172. openhands_automation-1.0.0/frontend/src/mocks/node.ts +4 -0
  173. openhands_automation-1.0.0/frontend/src/query-client-config.ts +62 -0
  174. openhands_automation-1.0.0/frontend/src/root.tsx +35 -0
  175. openhands_automation-1.0.0/frontend/src/routes/automation-detail.tsx +98 -0
  176. openhands_automation-1.0.0/frontend/src/routes/automations-list.tsx +144 -0
  177. openhands_automation-1.0.0/frontend/src/routes/root-layout.tsx +119 -0
  178. openhands_automation-1.0.0/frontend/src/routes.ts +13 -0
  179. openhands_automation-1.0.0/frontend/src/stores/.gitkeep +0 -0
  180. openhands_automation-1.0.0/frontend/src/stores/user-store.ts +22 -0
  181. openhands_automation-1.0.0/frontend/src/tailwind.css +52 -0
  182. openhands_automation-1.0.0/frontend/src/types/.gitkeep +0 -0
  183. openhands_automation-1.0.0/frontend/src/types/automation.ts +52 -0
  184. openhands_automation-1.0.0/frontend/src/types/user.ts +9 -0
  185. openhands_automation-1.0.0/frontend/src/utils/custom-toast-handlers.tsx +42 -0
  186. openhands_automation-1.0.0/frontend/src/utils/generate-auth-url.ts +43 -0
  187. openhands_automation-1.0.0/frontend/src/utils/local-storage.ts +36 -0
  188. openhands_automation-1.0.0/frontend/src/utils/permissions.ts +8 -0
  189. openhands_automation-1.0.0/frontend/src/utils/toast-duration.ts +27 -0
  190. openhands_automation-1.0.0/frontend/src/utils/utils.ts +6 -0
  191. openhands_automation-1.0.0/frontend/tailwind.config.js +65 -0
  192. openhands_automation-1.0.0/frontend/tsconfig.json +30 -0
  193. openhands_automation-1.0.0/frontend/vite.config.ts +83 -0
  194. openhands_automation-1.0.0/frontend/vitest.setup.ts +39 -0
  195. openhands_automation-1.0.0/migrations/env.py +160 -0
  196. openhands_automation-1.0.0/migrations/script.py.mako +25 -0
  197. openhands_automation-1.0.0/migrations/versions/001_initial_schema.py +129 -0
  198. openhands_automation-1.0.0/migrations/versions/002_tarball_uploads.py +64 -0
  199. openhands_automation-1.0.0/migrations/versions/003_event_triggers.py +92 -0
  200. openhands_automation-1.0.0/migrations/versions/004_add_prompt_column.py +29 -0
  201. openhands_automation-1.0.0/migrations/versions/005_add_bash_command_id.py +37 -0
  202. openhands_automation-1.0.0/migrations/versions/006_add_model.py +25 -0
  203. openhands_automation-1.0.0/migrations/versions/007_add_automation_run_composite_indexes.py +44 -0
  204. openhands_automation-1.0.0/migrations/versions/008_add_kv_store.py +109 -0
  205. openhands_automation-1.0.0/migrations/versions/009_add_sandbox_cleanup_policy.py +47 -0
  206. openhands_automation-1.0.0/openhands/automation/__init__.py +3 -0
  207. openhands_automation-1.0.0/openhands/automation/app.py +332 -0
  208. openhands_automation-1.0.0/openhands/automation/auth.py +444 -0
  209. openhands_automation-1.0.0/openhands/automation/backends/__init__.py +68 -0
  210. openhands_automation-1.0.0/openhands/automation/backends/base.py +145 -0
  211. openhands_automation-1.0.0/openhands/automation/backends/cloud.py +332 -0
  212. openhands_automation-1.0.0/openhands/automation/backends/local.py +206 -0
  213. openhands_automation-1.0.0/openhands/automation/config.py +671 -0
  214. openhands_automation-1.0.0/openhands/automation/constants.py +86 -0
  215. openhands_automation-1.0.0/openhands/automation/db.py +249 -0
  216. openhands_automation-1.0.0/openhands/automation/dispatcher.py +496 -0
  217. openhands_automation-1.0.0/openhands/automation/event_router.py +192 -0
  218. openhands_automation-1.0.0/openhands/automation/event_schemas/__init__.py +132 -0
  219. openhands_automation-1.0.0/openhands/automation/event_schemas/bitbucket_data_center.py +38 -0
  220. openhands_automation-1.0.0/openhands/automation/event_schemas/custom.py +108 -0
  221. openhands_automation-1.0.0/openhands/automation/event_schemas/detection.py +136 -0
  222. openhands_automation-1.0.0/openhands/automation/event_schemas/github.py +483 -0
  223. openhands_automation-1.0.0/openhands/automation/event_schemas/jira_dc.py +35 -0
  224. openhands_automation-1.0.0/openhands/automation/exceptions.py +40 -0
  225. openhands_automation-1.0.0/openhands/automation/execution.py +591 -0
  226. openhands_automation-1.0.0/openhands/automation/filter_eval.py +192 -0
  227. openhands_automation-1.0.0/openhands/automation/kv_helpers.py +399 -0
  228. openhands_automation-1.0.0/openhands/automation/kv_metrics.py +114 -0
  229. openhands_automation-1.0.0/openhands/automation/kv_router.py +1139 -0
  230. openhands_automation-1.0.0/openhands/automation/kv_schemas.py +249 -0
  231. openhands_automation-1.0.0/openhands/automation/logger.py +115 -0
  232. openhands_automation-1.0.0/openhands/automation/middleware.py +69 -0
  233. openhands_automation-1.0.0/openhands/automation/models.py +392 -0
  234. openhands_automation-1.0.0/openhands/automation/preset_router.py +851 -0
  235. openhands_automation-1.0.0/openhands/automation/presets/__init__.py +9 -0
  236. openhands_automation-1.0.0/openhands/automation/presets/plugin/sdk_main.py +430 -0
  237. openhands_automation-1.0.0/openhands/automation/presets/plugin/setup.sh +48 -0
  238. openhands_automation-1.0.0/openhands/automation/presets/prompt/sdk_main.py +379 -0
  239. openhands_automation-1.0.0/openhands/automation/presets/prompt/setup.sh +48 -0
  240. openhands_automation-1.0.0/openhands/automation/router.py +548 -0
  241. openhands_automation-1.0.0/openhands/automation/scheduler.py +205 -0
  242. openhands_automation-1.0.0/openhands/automation/schemas.py +650 -0
  243. openhands_automation-1.0.0/openhands/automation/storage/__init__.py +18 -0
  244. openhands_automation-1.0.0/openhands/automation/storage/factory.py +36 -0
  245. openhands_automation-1.0.0/openhands/automation/storage/file_store.py +64 -0
  246. openhands_automation-1.0.0/openhands/automation/storage/google_cloud.py +195 -0
  247. openhands_automation-1.0.0/openhands/automation/storage/local.py +125 -0
  248. openhands_automation-1.0.0/openhands/automation/storage/s3.py +340 -0
  249. openhands_automation-1.0.0/openhands/automation/trigger_matcher.py +89 -0
  250. openhands_automation-1.0.0/openhands/automation/uploads.py +323 -0
  251. openhands_automation-1.0.0/openhands/automation/utils/__init__.py +26 -0
  252. openhands_automation-1.0.0/openhands/automation/utils/agent_server.py +184 -0
  253. openhands_automation-1.0.0/openhands/automation/utils/api_key.py +98 -0
  254. openhands_automation-1.0.0/openhands/automation/utils/cron.py +143 -0
  255. openhands_automation-1.0.0/openhands/automation/utils/kv.py +218 -0
  256. openhands_automation-1.0.0/openhands/automation/utils/log_context.py +31 -0
  257. openhands_automation-1.0.0/openhands/automation/utils/model_profiles.py +39 -0
  258. openhands_automation-1.0.0/openhands/automation/utils/run.py +268 -0
  259. openhands_automation-1.0.0/openhands/automation/utils/sandbox.py +211 -0
  260. openhands_automation-1.0.0/openhands/automation/utils/tarball_validation.py +170 -0
  261. openhands_automation-1.0.0/openhands/automation/utils/time.py +41 -0
  262. openhands_automation-1.0.0/openhands/automation/utils/webhook.py +237 -0
  263. openhands_automation-1.0.0/openhands/automation/watchdog.py +328 -0
  264. openhands_automation-1.0.0/openhands/automation/webhook_router.py +275 -0
  265. openhands_automation-1.0.0/pyproject.toml +133 -0
  266. openhands_automation-1.0.0/scripts/test_automation.py +190 -0
  267. openhands_automation-1.0.0/scripts/test_kv_e2e.py +955 -0
  268. openhands_automation-1.0.0/scripts/test_tarball/main.py +131 -0
  269. openhands_automation-1.0.0/scripts/test_tarball/setup.sh +40 -0
  270. openhands_automation-1.0.0/tests/__init__.py +0 -0
  271. openhands_automation-1.0.0/tests/conftest.py +205 -0
  272. openhands_automation-1.0.0/tests/test_ab_testing_integration.py +526 -0
  273. openhands_automation-1.0.0/tests/test_auth.py +1087 -0
  274. openhands_automation-1.0.0/tests/test_backends.py +554 -0
  275. openhands_automation-1.0.0/tests/test_cancel_run.py +131 -0
  276. openhands_automation-1.0.0/tests/test_config.py +434 -0
  277. openhands_automation-1.0.0/tests/test_cors.py +193 -0
  278. openhands_automation-1.0.0/tests/test_db.py +332 -0
  279. openhands_automation-1.0.0/tests/test_disable_automation.py +525 -0
  280. openhands_automation-1.0.0/tests/test_dispatcher.py +813 -0
  281. openhands_automation-1.0.0/tests/test_event_router.py +538 -0
  282. openhands_automation-1.0.0/tests/test_event_schemas.py +1006 -0
  283. openhands_automation-1.0.0/tests/test_execution.py +485 -0
  284. openhands_automation-1.0.0/tests/test_filter_eval.py +282 -0
  285. openhands_automation-1.0.0/tests/test_frontend.py +188 -0
  286. openhands_automation-1.0.0/tests/test_health.py +77 -0
  287. openhands_automation-1.0.0/tests/test_kv_batch.py +433 -0
  288. openhands_automation-1.0.0/tests/test_kv_concurrency.py +156 -0
  289. openhands_automation-1.0.0/tests/test_kv_helpers.py +629 -0
  290. openhands_automation-1.0.0/tests/test_kv_router.py +832 -0
  291. openhands_automation-1.0.0/tests/test_local_mode.py +384 -0
  292. openhands_automation-1.0.0/tests/test_openapi.py +30 -0
  293. openhands_automation-1.0.0/tests/test_preset_router.py +1672 -0
  294. openhands_automation-1.0.0/tests/test_router.py +2169 -0
  295. openhands_automation-1.0.0/tests/test_scheduler.py +953 -0
  296. openhands_automation-1.0.0/tests/test_schemas.py +137 -0
  297. openhands_automation-1.0.0/tests/test_storage.py +240 -0
  298. openhands_automation-1.0.0/tests/test_storage_integration.py +252 -0
  299. openhands_automation-1.0.0/tests/test_storage_local.py +362 -0
  300. openhands_automation-1.0.0/tests/test_storage_s3.py +399 -0
  301. openhands_automation-1.0.0/tests/test_storage_s3_integration.py +324 -0
  302. openhands_automation-1.0.0/tests/test_tarball_validation.py +333 -0
  303. openhands_automation-1.0.0/tests/test_test_tarball_setup.py +21 -0
  304. openhands_automation-1.0.0/tests/test_uploads.py +192 -0
  305. openhands_automation-1.0.0/tests/test_uploads_integration.py +227 -0
  306. openhands_automation-1.0.0/tests/test_watchdog.py +378 -0
  307. openhands_automation-1.0.0/tests/test_webhook_router.py +304 -0
  308. openhands_automation-1.0.0/tests/test_webhook_utils.py +322 -0
  309. openhands_automation-1.0.0/uv.lock +4181 -0
@@ -0,0 +1,32 @@
1
+ # Version control
2
+ .git
3
+ .gitignore
4
+
5
+ # CI/CD
6
+ .github
7
+
8
+ # Python
9
+ .venv
10
+ __pycache__
11
+ *.pyc
12
+ .pytest_cache
13
+ .ruff_cache
14
+ .mypy_cache
15
+
16
+ # Frontend (built inside Docker via multi-stage)
17
+ frontend/node_modules
18
+ frontend/build
19
+ frontend/coverage
20
+ frontend/test-results
21
+ frontend/playwright-report
22
+ frontend/.react-router
23
+
24
+ # IDE / editor
25
+ .vscode
26
+ .idea
27
+ *.swp
28
+
29
+ # Misc
30
+ .pr
31
+ .stakpak
32
+ .pre-commit-config.yaml
@@ -0,0 +1,56 @@
1
+ ---
2
+ # .github/workflows/precommit.yml
3
+ name: ci
4
+
5
+ on:
6
+ push:
7
+ branches: [main]
8
+ pull_request:
9
+ branches: ['**']
10
+
11
+ jobs:
12
+ backend:
13
+ runs-on: ubuntu-24.04
14
+
15
+ steps:
16
+ - name: Checkout code
17
+ uses: actions/checkout@v5
18
+
19
+ - name: Set up Python
20
+ uses: actions/setup-python@v6
21
+ with:
22
+ python-version: '3.12'
23
+
24
+ - name: Install uv
25
+ uses: astral-sh/setup-uv@v7
26
+
27
+ - name: Install dependencies
28
+ run: uv sync --frozen --group dev
29
+
30
+ - name: Run pre-commit (all files)
31
+ run: uv run pre-commit run --all-files --show-diff-on-failure
32
+
33
+ frontend:
34
+ runs-on: ubuntu-24.04
35
+ defaults:
36
+ run:
37
+ working-directory: frontend
38
+ steps:
39
+ - uses: actions/checkout@v5
40
+
41
+ - name: Setup Node
42
+ uses: actions/setup-node@v3
43
+ with:
44
+ node-version: 24
45
+
46
+ - name: NPM dependencies
47
+ run: npm ci
48
+
49
+ - name: Lint
50
+ run: npm run lint
51
+
52
+ - name: Test
53
+ run: npm run test
54
+
55
+ - name: Build
56
+ run: npm run build
@@ -0,0 +1,102 @@
1
+ ---
2
+ # Workflow that builds and pushes the automation docker image to ghcr.io
3
+ name: Docker
4
+
5
+ on:
6
+ push:
7
+ branches:
8
+ - main
9
+ tags:
10
+ - '*'
11
+ pull_request:
12
+ workflow_dispatch:
13
+ inputs:
14
+ reason:
15
+ description: Reason for manual trigger
16
+ required: true
17
+ default: ''
18
+
19
+ # If triggered by a PR, it will be in the same group. However, each commit on main will be in its own unique group
20
+ concurrency:
21
+ group: ${{ github.workflow }}-${{ (github.head_ref && github.ref) || github.run_id }}
22
+ cancel-in-progress: true
23
+
24
+ env:
25
+ REGISTRY: ghcr.io
26
+ IMAGE_NAME: openhands/automation
27
+ RELEVANT_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
28
+
29
+ jobs:
30
+ ghcr_build:
31
+ name: Build and Push Automation Image
32
+ runs-on: ubuntu-24.04
33
+ permissions:
34
+ contents: read
35
+ packages: write
36
+ steps:
37
+ - name: Checkout
38
+ uses: actions/checkout@v4
39
+ with:
40
+ ref: ${{ github.event.pull_request.head.sha }}
41
+
42
+ - name: Set up Docker Buildx
43
+ uses: docker/setup-buildx-action@v3
44
+ with:
45
+ driver-opts: network=host
46
+
47
+ - name: Login to GHCR
48
+ uses: docker/login-action@v3
49
+ with:
50
+ registry: ${{ env.REGISTRY }}
51
+ username: ${{ github.repository_owner }}
52
+ password: ${{ secrets.GITHUB_TOKEN }}
53
+
54
+ - name: Get short SHA
55
+ id: short_sha
56
+ run: echo "SHORT_SHA=$(echo ${{ env.RELEVANT_SHA }} | cut -c1-7)" >> $GITHUB_OUTPUT
57
+
58
+ - name: Extract metadata (tags, labels) for Docker
59
+ id: meta
60
+ uses: docker/metadata-action@v5
61
+ with:
62
+ images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
63
+ tags: |
64
+ type=ref,event=branch
65
+ type=ref,event=pr
66
+ type=sha
67
+ type=sha,format=long
68
+ type=semver,pattern={{version}}
69
+ type=semver,pattern={{major}}.{{minor}}
70
+ type=semver,pattern={{major}}
71
+ flavor: |
72
+ latest=auto
73
+ prefix=
74
+ suffix=
75
+ env:
76
+ DOCKER_METADATA_PR_HEAD_SHA: true
77
+
78
+ - name: Build and push Docker image
79
+ if: '!github.event.pull_request.head.repo.fork'
80
+ uses: docker/build-push-action@v5
81
+ with:
82
+ context: .
83
+ file: containers/Dockerfile
84
+ push: true
85
+ tags: ${{ steps.meta.outputs.tags }}
86
+ labels: ${{ steps.meta.outputs.labels }}
87
+ platforms: linux/amd64
88
+ cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache
89
+ cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:buildcache,mode=max
90
+ provenance: true
91
+ sbom: true
92
+
93
+ # Forked repos can't push to GHCR, so we just build to verify the image builds
94
+ - name: Build Docker image (fork)
95
+ if: github.event.pull_request.head.repo.fork
96
+ uses: docker/build-push-action@v5
97
+ with:
98
+ context: .
99
+ file: containers/Dockerfile
100
+ push: false
101
+ tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ env.RELEVANT_SHA }}
102
+ platforms: linux/amd64
@@ -0,0 +1,142 @@
1
+ ---
2
+ name: PR Artifacts
3
+
4
+ on:
5
+ workflow_dispatch: # Manual trigger for testing
6
+ pull_request:
7
+ types: [opened, synchronize, reopened]
8
+ branches: [main]
9
+ pull_request_review:
10
+ types: [submitted]
11
+
12
+ jobs:
13
+ # Auto-remove .pr/ directory when a reviewer approves
14
+ cleanup-on-approval:
15
+ concurrency:
16
+ group: cleanup-pr-artifacts-${{ github.event.pull_request.number }}
17
+ cancel-in-progress: false
18
+ if: github.event_name == 'pull_request_review' && github.event.review.state == 'approved'
19
+ runs-on: ubuntu-latest
20
+ permissions:
21
+ contents: write
22
+ pull-requests: write
23
+ steps:
24
+ - name: Check if fork PR
25
+ id: check-fork
26
+ env:
27
+ PR_HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }}
28
+ PR_BASE_REPO: ${{ github.event.pull_request.base.repo.full_name }}
29
+ run: |
30
+ if [ "$PR_HEAD_REPO" != "$PR_BASE_REPO" ]; then
31
+ echo "is_fork=true" >> $GITHUB_OUTPUT
32
+ echo "::notice::Fork PR detected - skipping auto-cleanup (manual removal required)"
33
+ else
34
+ echo "is_fork=false" >> $GITHUB_OUTPUT
35
+ fi
36
+
37
+ # Use PAT so the push triggers CI workflows that will complete and
38
+ # satisfy branch protection. We can't use [skip ci] because GitHub Apps
39
+ # can create stuck checks that block merging.
40
+ - uses: actions/checkout@v6
41
+ if: steps.check-fork.outputs.is_fork == 'false'
42
+ with:
43
+ ref: ${{ github.event.pull_request.head.ref }}
44
+ token: ${{ secrets.OPENHANDS_BOT_GITHUB_PAT_PUBLIC }}
45
+
46
+ - name: Remove .pr/ directory
47
+ id: remove
48
+ if: steps.check-fork.outputs.is_fork == 'false'
49
+ run: |
50
+ if [ -d ".pr" ]; then
51
+ git config user.name "allhands-bot"
52
+ git config user.email "allhands-bot@users.noreply.github.com"
53
+ git rm -rf .pr/
54
+ git commit -m "chore: Remove PR-only artifacts [automated]"
55
+ git push || {
56
+ echo "::error::Failed to push cleanup commit. Check branch protection rules."
57
+ exit 1
58
+ }
59
+ echo "removed=true" >> $GITHUB_OUTPUT
60
+ echo "::notice::Removed .pr/ directory"
61
+ else
62
+ echo "removed=false" >> $GITHUB_OUTPUT
63
+ echo "::notice::No .pr/ directory to remove"
64
+ fi
65
+
66
+ - name: Update PR comment after cleanup
67
+ if: steps.check-fork.outputs.is_fork == 'false' && steps.remove.outputs.removed == 'true'
68
+ uses: actions/github-script@v9
69
+ with:
70
+ script: |
71
+ const marker = '<!-- pr-artifacts-notice -->';
72
+ const body = `${marker}
73
+ ✅ **PR Artifacts Cleaned Up**
74
+
75
+ The \`.pr/\` directory has been automatically removed.
76
+ `;
77
+
78
+ const { data: comments } = await github.rest.issues.listComments({
79
+ owner: context.repo.owner,
80
+ repo: context.repo.repo,
81
+ issue_number: context.issue.number,
82
+ });
83
+
84
+ const existing = comments.find(c => c.body.includes(marker));
85
+ if (existing) {
86
+ await github.rest.issues.updateComment({
87
+ owner: context.repo.owner,
88
+ repo: context.repo.repo,
89
+ comment_id: existing.id,
90
+ body: body,
91
+ });
92
+ }
93
+
94
+ # Warn if .pr/ directory exists (will be auto-removed on approval)
95
+ check-pr-artifacts:
96
+ if: github.event_name == 'pull_request'
97
+ runs-on: ubuntu-latest
98
+ permissions:
99
+ contents: read
100
+ pull-requests: write
101
+ steps:
102
+ - uses: actions/checkout@v6
103
+
104
+ - name: Check for .pr/ directory
105
+ id: check
106
+ run: |
107
+ if [ -d ".pr" ]; then
108
+ echo "exists=true" >> $GITHUB_OUTPUT
109
+ echo "::warning::the .pr/ directory is a temporary directory, intended by this repository policy to be used to show live tests artifacts for review. It will be automatically removed when the PR is approved. For fork PRs, manual removal is required before merging."
110
+ else
111
+ echo "exists=false" >> $GITHUB_OUTPUT
112
+ fi
113
+
114
+ - name: Post or update PR comment
115
+ if: steps.check.outputs.exists == 'true'
116
+ uses: actions/github-script@v9
117
+ with:
118
+ script: |
119
+ const marker = '<!-- pr-artifacts-notice -->';
120
+ const body = `${marker}
121
+ 📁 **PR Artifacts Notice**
122
+
123
+ This PR contains a \`.pr/\` directory with PR-specific documents. This directory will be **automatically removed** when the PR is approved.
124
+
125
+ > For fork PRs: Manual removal is required before merging.
126
+ `;
127
+
128
+ const { data: comments } = await github.rest.issues.listComments({
129
+ owner: context.repo.owner,
130
+ repo: context.repo.repo,
131
+ issue_number: context.issue.number,
132
+ });
133
+
134
+ const existing = comments.find(c => c.body.includes(marker));
135
+ if (!existing) {
136
+ await github.rest.issues.createComment({
137
+ owner: context.repo.owner,
138
+ repo: context.repo.repo,
139
+ issue_number: context.issue.number,
140
+ body: body,
141
+ });
142
+ }
@@ -0,0 +1,135 @@
1
+ ---
2
+ # Workflow that prepares a release by bumping the version and opening a PR.
3
+ # Triggered manually via workflow_dispatch with a version input.
4
+ # After the PR is merged, create and push a tag to trigger the actual release.
5
+ name: Prepare Release
6
+
7
+ on:
8
+ workflow_dispatch:
9
+ inputs:
10
+ version:
11
+ description: Version to release (e.g., 1.0.0, 1.0.0a3, 1.0.0b1)
12
+ required: true
13
+ type: string
14
+
15
+ jobs:
16
+ prepare-release:
17
+ name: Prepare Release PR
18
+ runs-on: ubuntu-24.04
19
+ permissions:
20
+ contents: write
21
+ pull-requests: write
22
+ steps:
23
+ - name: Validate version format
24
+ run: |
25
+ VERSION="${{ inputs.version }}"
26
+ # Basic PEP 440 validation (allows: X.Y.Z, X.Y.ZaN, X.Y.ZbN, X.Y.ZrcN)
27
+ if ! echo "$VERSION" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+(a[0-9]+|b[0-9]+|rc[0-9]+)?$'; then
28
+ echo "::error::Invalid version format: $VERSION"
29
+ echo "Expected format: X.Y.Z, X.Y.ZaN (alpha), X.Y.ZbN (beta), or X.Y.ZrcN (release candidate)"
30
+ exit 1
31
+ fi
32
+
33
+ - name: Checkout
34
+ uses: actions/checkout@v4
35
+ with:
36
+ token: ${{ secrets.OPENHANDS_BOT_GITHUB_PAT_PUBLIC }}
37
+ fetch-depth: 0
38
+
39
+ - name: Install uv
40
+ uses: astral-sh/setup-uv@v7
41
+ with:
42
+ version: latest
43
+
44
+ - name: Set up Python
45
+ run: uv python install 3.12
46
+
47
+ - name: Get current version
48
+ id: current
49
+ run: |
50
+ CURRENT=$(grep -oP '^version = "\K[^"]+' pyproject.toml)
51
+ echo "version=$CURRENT" >> $GITHUB_OUTPUT
52
+ echo "Current version: $CURRENT"
53
+
54
+ - name: Bump version
55
+ run: |
56
+ VERSION="${{ inputs.version }}"
57
+ CURRENT="${{ steps.current.outputs.version }}"
58
+ if [ "$CURRENT" = "$VERSION" ]; then
59
+ echo "::error::Version $VERSION is already set in pyproject.toml"
60
+ exit 1
61
+ fi
62
+ sed -i "s/^version = \".*\"/version = \"$VERSION\"/" pyproject.toml
63
+ if ! grep -q "^version = \"$VERSION\"" pyproject.toml; then
64
+ echo "::error::Failed to update version in pyproject.toml"
65
+ exit 1
66
+ fi
67
+ echo "Updated pyproject.toml to version $VERSION"
68
+ grep '^version = ' pyproject.toml
69
+
70
+ - name: Update lockfile
71
+ run: |
72
+ uv lock
73
+ if ! git diff --quiet uv.lock; then
74
+ echo "Lock file updated successfully"
75
+ else
76
+ echo "::warning::Lock file unchanged - verify dependencies are correct"
77
+ fi
78
+
79
+ - name: Configure git
80
+ run: |
81
+ git config user.name "all-hands-bot"
82
+ git config user.email "all-hands-bot@all-hands.dev"
83
+
84
+ - name: Create branch and commit
85
+ id: commit
86
+ run: |
87
+ VERSION="${{ inputs.version }}"
88
+ BRANCH_NAME="release/v$VERSION"
89
+
90
+ git checkout -b "$BRANCH_NAME" 2>/dev/null || {
91
+ echo "::error::Branch $BRANCH_NAME already exists. Delete it or use a different version."
92
+ exit 1
93
+ }
94
+ git add pyproject.toml uv.lock
95
+ git commit -m "chore: bump version to $VERSION"
96
+ git push -u origin "$BRANCH_NAME"
97
+
98
+ echo "branch=$BRANCH_NAME" >> $GITHUB_OUTPUT
99
+
100
+ - name: Create Pull Request
101
+ env:
102
+ GH_TOKEN: ${{ secrets.OPENHANDS_BOT_GITHUB_PAT_PUBLIC }}
103
+ run: |
104
+ VERSION="${{ inputs.version }}"
105
+ CURRENT="${{ steps.current.outputs.version }}"
106
+
107
+ gh pr create \
108
+ --title "chore: release v$VERSION" \
109
+ --body "## Release v$VERSION
110
+
111
+ This PR bumps the version from \`$CURRENT\` to \`$VERSION\`.
112
+
113
+ ### Changes
114
+ - Updated \`pyproject.toml\` version to \`$VERSION\`
115
+ - Regenerated \`uv.lock\`
116
+
117
+ ### After Merging
118
+
119
+ Once this PR is merged, create and push the release tag:
120
+
121
+ \`\`\`bash
122
+ git checkout main
123
+ git pull origin main
124
+ git tag $VERSION
125
+ git push origin $VERSION
126
+ \`\`\`
127
+
128
+ This will trigger:
129
+ - \`pypi-release.yml\` — publishes \`openhands-automation\` to PyPI
130
+ - \`tag-image.yml\` — tags the Docker image with \`$VERSION\`
131
+
132
+ ---
133
+ *This PR was automatically created by the Prepare Release workflow.*" \
134
+ --base main \
135
+ --head "${{ steps.commit.outputs.branch }}"
@@ -0,0 +1,90 @@
1
+ ---
2
+ # Promotes each successful main build's automation image to the development
3
+ # environment by dispatching to OpenHands/saas-deploy, which runs the bump.
4
+ # Runs after the Docker workflow succeeds on main; can also be run manually
5
+ # (from main) to re-promote a known-good image tag.
6
+
7
+ name: Promote to development
8
+
9
+ on:
10
+ workflow_dispatch:
11
+ inputs:
12
+ image-tag:
13
+ description: Image tag to promote (defaults to sha-<short SHA> for the selected ref)
14
+ required: false
15
+ type: string
16
+ workflow_run:
17
+ workflows: [Docker]
18
+ types: [completed]
19
+ branches: [main]
20
+
21
+ # Queue promotions so an earlier main commit deploys before a later one.
22
+ concurrency:
23
+ group: promote-to-development-automation
24
+ cancel-in-progress: false
25
+
26
+ jobs:
27
+ promote:
28
+ # Only promote when the Docker build actually succeeded, or when run manually.
29
+ # The Docker workflow also runs on pull_request, and the workflow_run branches
30
+ # filter matches the *head* branch - a fork PR from a branch named "main"
31
+ # would slip through - so additionally require a push event.
32
+ if: >-
33
+ ${{ github.event_name == 'workflow_dispatch' ||
34
+ (github.event.workflow_run.conclusion == 'success' &&
35
+ github.event.workflow_run.event == 'push') }}
36
+ runs-on: ubuntu-24.04
37
+ timeout-minutes: 10
38
+ environment: dev-deploy
39
+ permissions:
40
+ contents: read
41
+ steps:
42
+ - name: Compute image tag
43
+ id: tag
44
+ # Matches docker/metadata-action type=sha (7-char short SHA) emitted by
45
+ # the Docker workflow.
46
+ env:
47
+ DISPATCH_IMAGE_TAG: ${{ github.event.inputs['image-tag'] }}
48
+ SHA: ${{ github.event.workflow_run.head_sha || github.sha }}
49
+ run: |
50
+ if [ -n "$DISPATCH_IMAGE_TAG" ]; then
51
+ echo "tag=$DISPATCH_IMAGE_TAG" >> "$GITHUB_OUTPUT"
52
+ else
53
+ echo "tag=sha-${SHA::7}" >> "$GITHUB_OUTPUT"
54
+ fi
55
+
56
+ - name: Mint token
57
+ id: app-token
58
+ uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0
59
+ with:
60
+ app-id: ${{ secrets.SAAS_DEPLOY_DISPATCHER_APP_ID }}
61
+ private-key: ${{ secrets.SAAS_DEPLOY_DISPATCHER_APP_PRIVATE_KEY }}
62
+ owner: OpenHands
63
+ repositories: saas-deploy
64
+ permission-contents: write
65
+
66
+ - name: Dispatch promotion to saas-deploy
67
+ env:
68
+ GH_TOKEN: ${{ steps.app-token.outputs.token }}
69
+ IMAGE_TAG: ${{ steps.tag.outputs.tag }}
70
+ SOURCE_SHA: ${{ github.event.workflow_run.head_sha || github.sha }}
71
+ run: |
72
+ gh api /repos/OpenHands/saas-deploy/dispatches \
73
+ -f event_type=promote-to-development \
74
+ -F client_payload[release]=automation \
75
+ -F client_payload[tag-path]=.openhands.automation.image.tag \
76
+ -F client_payload[image-tag]="$IMAGE_TAG" \
77
+ -F client_payload[source-repo]="$GITHUB_REPOSITORY" \
78
+ -F client_payload[source-sha]="$SOURCE_SHA"
79
+ echo "Dispatched promote-to-development for automation ${IMAGE_TAG} (${SOURCE_SHA})."
80
+
81
+ - name: Annotate dispatch failure
82
+ if: ${{ failure() }}
83
+ env:
84
+ IMAGE_TAG: ${{ steps.tag.outputs.tag }}
85
+ HEAD_SHA: ${{ github.event.workflow_run.head_sha || github.sha }}
86
+ SOURCE_RUN_URL: ${{ github.event.workflow_run.html_url }}
87
+ run: |
88
+ image_tag="${IMAGE_TAG:-sha-${HEAD_SHA::7}}"
89
+ source="${SOURCE_RUN_URL:-manual workflow_dispatch run}"
90
+ echo "::error title=Promote to development failed::Failed to dispatch promotion of automation ${image_tag} (${HEAD_SHA}) to development. Source: ${source}"
@@ -0,0 +1,40 @@
1
+ ---
2
+ # Publishes the openhands-automation PyPI package using trusted publishing (OIDC).
3
+ # Triggered when a tag is pushed or manually via workflow_dispatch.
4
+ # Requires trusted publishing to be configured in PyPI project settings.
5
+ name: Publish PyPI Package
6
+
7
+ on:
8
+ push:
9
+ tags:
10
+ - '*'
11
+ workflow_dispatch:
12
+ inputs:
13
+ reason:
14
+ description: Reason for manual trigger
15
+ required: true
16
+ default: ''
17
+
18
+ jobs:
19
+ release:
20
+ runs-on: ubuntu-24.04
21
+ permissions:
22
+ id-token: write
23
+ contents: read
24
+ steps:
25
+ - name: Checkout
26
+ uses: actions/checkout@v4
27
+
28
+ - name: Install uv
29
+ uses: astral-sh/setup-uv@v7
30
+ with:
31
+ version: latest
32
+
33
+ - name: Set up Python
34
+ run: uv python install 3.12
35
+
36
+ - name: Build package
37
+ run: uv build
38
+
39
+ - name: Publish to PyPI
40
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,41 @@
1
+ ---
2
+ # Automated QA validation of PR changes using OpenHands.
3
+ #
4
+ # This workflow runs only when the `qa-this` label is added to a same-repo PR.
5
+ # Unlike code review, the QA agent executes the changed code and posts a
6
+ # functional verification report.
7
+ name: QA Changes by OpenHands
8
+
9
+ on:
10
+ pull_request:
11
+ types: [labeled]
12
+
13
+ permissions:
14
+ contents: read
15
+ pull-requests: write
16
+ issues: write
17
+
18
+ jobs:
19
+ qa-changes:
20
+ # Only run for same-repo PRs (secrets are not available to forks), and
21
+ # only when explicitly triggered by the `qa-this` label.
22
+ if: |
23
+ github.event.pull_request.head.repo.full_name == github.repository &&
24
+ github.event.label.name == 'qa-this'
25
+ concurrency:
26
+ group: qa-changes-${{ github.event.pull_request.number }}
27
+ cancel-in-progress: true
28
+ runs-on: ubuntu-24.04
29
+ timeout-minutes: 30
30
+ steps:
31
+ - name: Run QA Changes
32
+ uses: OpenHands/extensions/plugins/qa-changes@main
33
+ with:
34
+ llm-model: litellm_proxy/openai/gpt-5.5
35
+ llm-base-url: https://llm-proxy.app.all-hands.dev
36
+ max-budget: '10.0'
37
+ timeout-minutes: '30'
38
+ max-iterations: '500'
39
+ llm-api-key: ${{ secrets.LLM_API_KEY }}
40
+ github-token: ${{ secrets.OPENHANDS_BOT_GITHUB_PAT_PUBLIC || github.token }}
41
+ lmnr-api-key: ${{ secrets.LMNR_SKILLS_API_KEY }}