@openhands/extensions 0.1.0 → 0.2.0

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 (347) hide show
  1. package/.agents/skills/custom-codereview-guide.md +25 -0
  2. package/.github/pull_request_template.md +38 -0
  3. package/.github/release.yml +14 -0
  4. package/.github/workflows/check-extensions.yml +72 -0
  5. package/.github/workflows/npm-publish.yml +89 -0
  6. package/.github/workflows/pr.yml +30 -0
  7. package/.github/workflows/release.yml +24 -0
  8. package/.github/workflows/tests.yml +25 -0
  9. package/.github/workflows/vulnerability-scan.yml +87 -0
  10. package/.release-please-manifest.json +3 -0
  11. package/AGENTS.md +132 -0
  12. package/README.md +10 -0
  13. package/analysis_results.md +162 -0
  14. package/marketplaces/large-codebase.json +66 -0
  15. package/marketplaces/openhands-extensions.json +682 -0
  16. package/package.json +4 -10
  17. package/plugins/README.md +30 -0
  18. package/plugins/city-weather/.plugin/plugin.json +13 -0
  19. package/plugins/city-weather/README.md +145 -0
  20. package/plugins/city-weather/commands/now.md +56 -0
  21. package/plugins/cobol-modernization/.plugin/plugin.json +19 -0
  22. package/plugins/cobol-modernization/README.md +201 -0
  23. package/plugins/cobol-modernization/references/troubleshooting.md +18 -0
  24. package/plugins/cobol-modernization/skills/build-setup/SKILL.md +78 -0
  25. package/plugins/cobol-modernization/skills/build-setup/scripts/install-gnucobol.sh +32 -0
  26. package/plugins/cobol-modernization/skills/cobol-modernization-overview/SKILL.md +113 -0
  27. package/plugins/cobol-modernization/skills/mainfraime-removal/SKILL.md +62 -0
  28. package/plugins/cobol-modernization/skills/mainfraime-removal/references/cics-transformation-examples.md +45 -0
  29. package/plugins/cobol-modernization/skills/mainframe-planning/SKILL.md +78 -0
  30. package/plugins/cobol-modernization/skills/to-java-migration/SKILL.md +59 -0
  31. package/plugins/cobol-modernization/skills/to-java-migration/references/cobol-to-java-example.md +58 -0
  32. package/plugins/cobol-modernization/skills/to-java-migration/references/datatype-mappings.md +19 -0
  33. package/plugins/issue-duplicate-checker/.plugin/plugin.json +13 -0
  34. package/plugins/issue-duplicate-checker/README.md +51 -0
  35. package/plugins/issue-duplicate-checker/action.yml +349 -0
  36. package/plugins/issue-duplicate-checker/scripts/auto_close_duplicate_issues.py +569 -0
  37. package/plugins/issue-duplicate-checker/scripts/issue_duplicate_check_openhands.py +681 -0
  38. package/plugins/issue-duplicate-checker/scripts/post_duplicate_notice.js +220 -0
  39. package/plugins/issue-duplicate-checker/scripts/remove_duplicate_candidate_label.js +27 -0
  40. package/plugins/magic-test/.plugin/plugin.json +13 -0
  41. package/plugins/magic-test/skills/magic-word/SKILL.md +33 -0
  42. package/plugins/migration-scoring/.plugin/plugin.json +19 -0
  43. package/plugins/migration-scoring/README.md +244 -0
  44. package/plugins/migration-scoring/skills/migration-mapping/SKILL.md +72 -0
  45. package/plugins/migration-scoring/skills/migration-report/SKILL.md +118 -0
  46. package/plugins/migration-scoring/skills/migration-scoring-overview/SKILL.md +126 -0
  47. package/plugins/migration-scoring/skills/score-quality/SKILL.md +54 -0
  48. package/plugins/migration-scoring/skills/score-quality/references/scoring-criteria.md +30 -0
  49. package/plugins/migration-scoring/skills/score-style/SKILL.md +106 -0
  50. package/plugins/onboarding/.plugin/plugin.json +20 -0
  51. package/plugins/onboarding/README.md +30 -0
  52. package/plugins/onboarding/references/criteria.md +144 -0
  53. package/plugins/onboarding/skills/agent-readiness-report/README.md +23 -0
  54. package/plugins/onboarding/skills/agent-readiness-report/SKILL.md +122 -0
  55. package/plugins/onboarding/skills/agent-readiness-report/scripts/scan_agent_instructions.sh +88 -0
  56. package/plugins/onboarding/skills/agent-readiness-report/scripts/scan_build_env.sh +114 -0
  57. package/plugins/onboarding/skills/agent-readiness-report/scripts/scan_feedback_loops.sh +133 -0
  58. package/plugins/onboarding/skills/agent-readiness-report/scripts/scan_policy.sh +113 -0
  59. package/plugins/onboarding/skills/agent-readiness-report/scripts/scan_workflows.sh +127 -0
  60. package/plugins/onboarding/skills/improve-agent-readiness/README.md +19 -0
  61. package/plugins/onboarding/skills/improve-agent-readiness/SKILL.md +167 -0
  62. package/plugins/onboarding/skills/setup-agents-md/README.md +15 -0
  63. package/plugins/onboarding/skills/setup-agents-md/SKILL.md +150 -0
  64. package/plugins/onboarding/skills/setup-openhands/README.md +20 -0
  65. package/plugins/onboarding/skills/setup-openhands/SKILL.md +56 -0
  66. package/plugins/onboarding/skills/setup-pr-review/README.md +23 -0
  67. package/plugins/onboarding/skills/setup-pr-review/SKILL.md +72 -0
  68. package/plugins/openhands/.plugin/plugin.json +13 -0
  69. package/plugins/openhands/README.md +52 -0
  70. package/plugins/openhands/SKILL.md +61 -0
  71. package/plugins/openhands/commands/create.md +55 -0
  72. package/plugins/openhands/commands/openhands-cloud.md +8 -0
  73. package/plugins/openhands/scripts/run.sh +69 -0
  74. package/plugins/pr-review/.plugin/plugin.json +13 -0
  75. package/plugins/pr-review/README.md +393 -0
  76. package/plugins/pr-review/action.yml +298 -0
  77. package/plugins/pr-review/scripts/agent_script.py +1282 -0
  78. package/plugins/pr-review/scripts/evaluate_review.py +655 -0
  79. package/plugins/pr-review/scripts/prompt.py +260 -0
  80. package/plugins/pr-review/workflows/pr-review-by-openhands.yml +51 -0
  81. package/plugins/pr-review/workflows/pr-review-evaluation.yml +85 -0
  82. package/plugins/qa-changes/.plugin/plugin.json +11 -0
  83. package/plugins/qa-changes/README.md +185 -0
  84. package/plugins/qa-changes/action.yml +181 -0
  85. package/plugins/qa-changes/scripts/agent_script.py +406 -0
  86. package/plugins/qa-changes/scripts/evaluate_qa_changes.py +385 -0
  87. package/plugins/qa-changes/scripts/prompt.py +174 -0
  88. package/plugins/qa-changes/workflows/qa-changes-by-openhands.yml +50 -0
  89. package/plugins/qa-changes/workflows/qa-changes-evaluation.yml +85 -0
  90. package/plugins/release-notes/.plugin/plugin.json +19 -0
  91. package/plugins/release-notes/README.md +283 -0
  92. package/plugins/release-notes/SKILL.md +83 -0
  93. package/plugins/release-notes/action.yml +117 -0
  94. package/plugins/release-notes/commands/release-notes.md +8 -0
  95. package/plugins/release-notes/scripts/agent_script.py +292 -0
  96. package/plugins/release-notes/scripts/generate_release_notes.py +733 -0
  97. package/plugins/release-notes/scripts/prompt.py +90 -0
  98. package/plugins/release-notes/scripts/validate_release_notes.py +328 -0
  99. package/plugins/release-notes/workflows/release-notes.yml +76 -0
  100. package/plugins/vulnerability-remediation/.plugin/plugin.json +19 -0
  101. package/plugins/vulnerability-remediation/README.md +217 -0
  102. package/plugins/vulnerability-remediation/action.yml +187 -0
  103. package/plugins/vulnerability-remediation/scripts/scan_and_remediate.py +561 -0
  104. package/plugins/vulnerability-remediation/workflows/vulnerability-scan.yml +87 -0
  105. package/pyproject.toml +12 -0
  106. package/release-please-config.json +16 -0
  107. package/scripts/sync_extensions.py +494 -0
  108. package/scripts/sync_openhands_sdk_skill.py +264 -0
  109. package/skills/README.md +159 -0
  110. package/skills/add-javadoc/.plugin/plugin.json +18 -0
  111. package/skills/add-javadoc/README.md +40 -0
  112. package/skills/add-javadoc/SKILL.md +35 -0
  113. package/skills/add-javadoc/references/example.md +32 -0
  114. package/skills/add-skill/.plugin/plugin.json +18 -0
  115. package/skills/add-skill/README.md +67 -0
  116. package/skills/add-skill/SKILL.md +47 -0
  117. package/skills/add-skill/scripts/fetch_skill.py +259 -0
  118. package/skills/agent-creator/.plugin/plugin.json +20 -0
  119. package/skills/agent-creator/README.md +104 -0
  120. package/skills/agent-creator/SKILL.md +190 -0
  121. package/skills/agent-creator/commands/agent-creator.md +8 -0
  122. package/skills/agent-creator/references/fallback.md +117 -0
  123. package/skills/agent-memory/.plugin/plugin.json +18 -0
  124. package/skills/agent-memory/README.md +35 -0
  125. package/skills/agent-memory/SKILL.md +30 -0
  126. package/skills/agent-memory/commands/remember.md +8 -0
  127. package/skills/agent-sdk-builder/.plugin/plugin.json +18 -0
  128. package/skills/agent-sdk-builder/README.md +40 -0
  129. package/skills/agent-sdk-builder/SKILL.md +37 -0
  130. package/skills/agent-sdk-builder/commands/agent-builder.md +8 -0
  131. package/skills/azure-devops/.plugin/plugin.json +18 -0
  132. package/skills/azure-devops/README.md +55 -0
  133. package/skills/azure-devops/SKILL.md +50 -0
  134. package/skills/bitbucket/.plugin/plugin.json +17 -0
  135. package/skills/bitbucket/README.md +50 -0
  136. package/skills/bitbucket/SKILL.md +45 -0
  137. package/skills/code-review/.plugin/plugin.json +19 -0
  138. package/skills/code-review/README.md +18 -0
  139. package/skills/code-review/SKILL.md +208 -0
  140. package/skills/code-review/commands/codereview-roasted.md +8 -0
  141. package/skills/code-review/commands/codereview.md +8 -0
  142. package/skills/code-review/references/risk-evaluation.md +41 -0
  143. package/skills/code-review/references/supply-chain-security.md +31 -0
  144. package/skills/code-simplifier/.plugin/plugin.json +21 -0
  145. package/skills/code-simplifier/README.md +30 -0
  146. package/skills/code-simplifier/SKILL.md +91 -0
  147. package/skills/code-simplifier/commands/simplify.md +8 -0
  148. package/skills/code-simplifier/references/code-quality-review.md +86 -0
  149. package/skills/code-simplifier/references/code-reuse-review.md +63 -0
  150. package/skills/code-simplifier/references/efficiency-review.md +81 -0
  151. package/skills/datadog/.plugin/plugin.json +19 -0
  152. package/skills/datadog/README.md +100 -0
  153. package/skills/datadog/SKILL.md +95 -0
  154. package/skills/deno/.plugin/plugin.json +18 -0
  155. package/skills/deno/README.md +5 -0
  156. package/skills/deno/SKILL.md +99 -0
  157. package/skills/deno/references/README.md +6 -0
  158. package/skills/discord/.plugin/plugin.json +18 -0
  159. package/skills/discord/README.md +31 -0
  160. package/skills/discord/SKILL.md +109 -0
  161. package/skills/discord/__init__.py +0 -0
  162. package/skills/discord/references/REFERENCE.md +78 -0
  163. package/skills/discord/scripts/__init__.py +0 -0
  164. package/skills/discord/scripts/_http.py +127 -0
  165. package/skills/discord/scripts/post_webhook.py +106 -0
  166. package/skills/discord/scripts/send_message.py +102 -0
  167. package/skills/docker/.plugin/plugin.json +17 -0
  168. package/skills/docker/README.md +34 -0
  169. package/skills/docker/SKILL.md +29 -0
  170. package/skills/evidence-based-citations/.plugin/plugin.json +20 -0
  171. package/skills/evidence-based-citations/README.md +31 -0
  172. package/skills/evidence-based-citations/SKILL.md +59 -0
  173. package/skills/flarglebargle/.plugin/plugin.json +16 -0
  174. package/skills/flarglebargle/README.md +14 -0
  175. package/skills/flarglebargle/SKILL.md +9 -0
  176. package/skills/frontend-design/.plugin/plugin.json +21 -0
  177. package/skills/frontend-design/LICENSE.txt +177 -0
  178. package/skills/frontend-design/README.md +42 -0
  179. package/skills/frontend-design/SKILL.md +42 -0
  180. package/skills/github/.plugin/plugin.json +19 -0
  181. package/skills/github/README.md +42 -0
  182. package/skills/github/SKILL.md +106 -0
  183. package/skills/github-pr-review/.plugin/plugin.json +18 -0
  184. package/skills/github-pr-review/README.md +145 -0
  185. package/skills/github-pr-review/SKILL.md +148 -0
  186. package/skills/github-pr-review/commands/github-pr-review.md +8 -0
  187. package/skills/github-pr-reviewer/.plugin/plugin.json +20 -0
  188. package/skills/github-pr-reviewer/README.md +34 -0
  189. package/skills/github-pr-reviewer/SKILL.md +89 -0
  190. package/skills/github-pr-reviewer/commands/pr-reviewer:setup.md +8 -0
  191. package/skills/github-repo-monitor/.plugin/plugin.json +22 -0
  192. package/skills/github-repo-monitor/README.md +70 -0
  193. package/skills/github-repo-monitor/SKILL.md +316 -0
  194. package/skills/github-repo-monitor/commands/github-monitor:poll.md +8 -0
  195. package/skills/github-repo-monitor/references/github-api.md +241 -0
  196. package/skills/github-repo-monitor/references/state-schema.md +160 -0
  197. package/skills/github-repo-monitor/scripts/main.py +915 -0
  198. package/skills/github-repo-monitor/tests/test_main.py +400 -0
  199. package/skills/gitlab/.plugin/plugin.json +17 -0
  200. package/skills/gitlab/README.md +37 -0
  201. package/skills/gitlab/SKILL.md +32 -0
  202. package/skills/incident-retrospective/.plugin/plugin.json +21 -0
  203. package/skills/incident-retrospective/README.md +34 -0
  204. package/skills/incident-retrospective/SKILL.md +98 -0
  205. package/skills/incident-retrospective/commands/incident-retro:setup.md +8 -0
  206. package/skills/iterate/.plugin/plugin.json +13 -0
  207. package/skills/iterate/README.md +25 -0
  208. package/skills/iterate/SKILL.md +399 -0
  209. package/skills/iterate/commands/babysit.md +8 -0
  210. package/skills/iterate/commands/iterate.md +8 -0
  211. package/skills/iterate/commands/verify.md +8 -0
  212. package/skills/iterate/references/heuristics.md +58 -0
  213. package/skills/iterate/references/verification.md +96 -0
  214. package/skills/jupyter/.plugin/plugin.json +18 -0
  215. package/skills/jupyter/README.md +55 -0
  216. package/skills/jupyter/SKILL.md +50 -0
  217. package/skills/kubernetes/.plugin/plugin.json +18 -0
  218. package/skills/kubernetes/README.md +53 -0
  219. package/skills/kubernetes/SKILL.md +48 -0
  220. package/skills/learn-from-code-review/.plugin/plugin.json +19 -0
  221. package/skills/learn-from-code-review/README.md +64 -0
  222. package/skills/learn-from-code-review/SKILL.md +186 -0
  223. package/skills/learn-from-code-review/commands/learn-from-reviews.md +8 -0
  224. package/skills/linear/.plugin/plugin.json +19 -0
  225. package/skills/linear/README.md +58 -0
  226. package/skills/linear/SKILL.md +213 -0
  227. package/skills/linear-triage/.plugin/plugin.json +21 -0
  228. package/skills/linear-triage/README.md +34 -0
  229. package/skills/linear-triage/SKILL.md +91 -0
  230. package/skills/linear-triage/commands/linear-triage:setup.md +8 -0
  231. package/skills/notion/.plugin/plugin.json +17 -0
  232. package/skills/notion/README.md +114 -0
  233. package/skills/notion/SKILL.md +109 -0
  234. package/skills/npm/.plugin/plugin.json +17 -0
  235. package/skills/npm/README.md +14 -0
  236. package/skills/npm/SKILL.md +9 -0
  237. package/skills/openhands-api/.plugin/plugin.json +22 -0
  238. package/skills/openhands-api/README.md +48 -0
  239. package/skills/openhands-api/SKILL.md +399 -0
  240. package/skills/openhands-api/references/README.md +33 -0
  241. package/skills/openhands-api/references/TROUBLESHOOTING.md +81 -0
  242. package/skills/openhands-api/references/example_prompt.md +12 -0
  243. package/skills/openhands-api/scripts/openhands_api.py +606 -0
  244. package/skills/openhands-api/scripts/openhands_api.ts +252 -0
  245. package/skills/openhands-automation/.plugin/plugin.json +19 -0
  246. package/skills/openhands-automation/README.md +89 -0
  247. package/skills/openhands-automation/SKILL.md +875 -0
  248. package/skills/openhands-automation/commands/automation:create.md +8 -0
  249. package/skills/openhands-automation/references/ab-testing.md +185 -0
  250. package/skills/openhands-automation/references/custom-automation.md +644 -0
  251. package/skills/openhands-sdk/.plugin/plugin.json +20 -0
  252. package/skills/openhands-sdk/README.md +22 -0
  253. package/skills/openhands-sdk/SKILL.md +229 -0
  254. package/skills/openhands-sdk/commands/sdk.md +8 -0
  255. package/skills/pdflatex/.plugin/plugin.json +18 -0
  256. package/skills/pdflatex/README.md +39 -0
  257. package/skills/pdflatex/SKILL.md +34 -0
  258. package/skills/prd/.plugin/plugin.json +19 -0
  259. package/skills/prd/README.md +28 -0
  260. package/skills/prd/SKILL.md +237 -0
  261. package/skills/prd/commands/prd.md +8 -0
  262. package/skills/qa-changes/README.md +18 -0
  263. package/skills/qa-changes/SKILL.md +229 -0
  264. package/skills/qa-changes/commands/qa-changes.md +8 -0
  265. package/skills/release-notes/README.md +24 -0
  266. package/skills/release-notes/SKILL.md +19 -0
  267. package/skills/release-notes/commands/release-notes.md +8 -0
  268. package/skills/research-brief/.plugin/plugin.json +20 -0
  269. package/skills/research-brief/README.md +34 -0
  270. package/skills/research-brief/SKILL.md +99 -0
  271. package/skills/research-brief/commands/research-brief:setup.md +8 -0
  272. package/skills/security/.plugin/plugin.json +18 -0
  273. package/skills/security/README.md +38 -0
  274. package/skills/security/SKILL.md +33 -0
  275. package/skills/skill-creator/.plugin/plugin.json +17 -0
  276. package/skills/skill-creator/LICENSE.txt +202 -0
  277. package/skills/skill-creator/README.md +182 -0
  278. package/skills/skill-creator/SKILL.md +545 -0
  279. package/skills/skill-creator/references/output-patterns.md +82 -0
  280. package/skills/skill-creator/references/workflows.md +28 -0
  281. package/skills/skill-creator/scripts/init_skill.py +303 -0
  282. package/skills/skill-creator/scripts/quick_validate.py +95 -0
  283. package/skills/slack-channel-monitor/.plugin/plugin.json +21 -0
  284. package/skills/slack-channel-monitor/README.md +91 -0
  285. package/skills/slack-channel-monitor/SKILL.md +276 -0
  286. package/skills/slack-channel-monitor/commands/slack-monitor:poll.md +8 -0
  287. package/skills/slack-channel-monitor/references/slack-api.md +207 -0
  288. package/skills/slack-channel-monitor/references/state-schema.md +180 -0
  289. package/skills/slack-channel-monitor/scripts/main.py +962 -0
  290. package/skills/slack-standup-digest/.plugin/plugin.json +21 -0
  291. package/skills/slack-standup-digest/README.md +34 -0
  292. package/skills/slack-standup-digest/SKILL.md +92 -0
  293. package/skills/slack-standup-digest/commands/standup-digest:setup.md +8 -0
  294. package/skills/spark-version-upgrade/.plugin/plugin.json +20 -0
  295. package/skills/spark-version-upgrade/README.md +54 -0
  296. package/skills/spark-version-upgrade/SKILL.md +233 -0
  297. package/skills/ssh/.plugin/plugin.json +18 -0
  298. package/skills/ssh/README.md +140 -0
  299. package/skills/ssh/SKILL.md +135 -0
  300. package/skills/swift-linux/.plugin/plugin.json +17 -0
  301. package/skills/swift-linux/README.md +86 -0
  302. package/skills/swift-linux/SKILL.md +81 -0
  303. package/skills/theme-factory/.plugin/plugin.json +19 -0
  304. package/skills/theme-factory/LICENSE.txt +202 -0
  305. package/skills/theme-factory/README.md +58 -0
  306. package/skills/theme-factory/SKILL.md +59 -0
  307. package/skills/theme-factory/theme-showcase.pdf +0 -0
  308. package/skills/theme-factory/themes/arctic-frost.md +19 -0
  309. package/skills/theme-factory/themes/botanical-garden.md +19 -0
  310. package/skills/theme-factory/themes/desert-rose.md +19 -0
  311. package/skills/theme-factory/themes/forest-canopy.md +19 -0
  312. package/skills/theme-factory/themes/golden-hour.md +19 -0
  313. package/skills/theme-factory/themes/midnight-galaxy.md +19 -0
  314. package/skills/theme-factory/themes/modern-minimalist.md +19 -0
  315. package/skills/theme-factory/themes/ocean-depths.md +19 -0
  316. package/skills/theme-factory/themes/sunset-boulevard.md +19 -0
  317. package/skills/theme-factory/themes/tech-innovation.md +19 -0
  318. package/skills/uv/.plugin/plugin.json +18 -0
  319. package/skills/uv/README.md +5 -0
  320. package/skills/uv/SKILL.md +95 -0
  321. package/skills/uv/references/README.md +5 -0
  322. package/skills/vercel/.plugin/plugin.json +18 -0
  323. package/skills/vercel/README.md +108 -0
  324. package/skills/vercel/SKILL.md +103 -0
  325. package/tests/test_add_skill_installs_to_agents_dir.py +42 -0
  326. package/tests/test_catalogs.py +109 -0
  327. package/tests/test_code_review_risk_evaluation.py +94 -0
  328. package/tests/test_issue_duplicate_checker.py +240 -0
  329. package/tests/test_openhands_api_python.py +152 -0
  330. package/tests/test_plugin_manifest.py +83 -0
  331. package/tests/test_pr_review_diff_payload.py +202 -0
  332. package/tests/test_pr_review_feedback.py +263 -0
  333. package/tests/test_pr_review_prompt.py +152 -0
  334. package/tests/test_pr_review_review_context.py +253 -0
  335. package/tests/test_qa_changes.py +232 -0
  336. package/tests/test_qa_changes_evaluation.py +259 -0
  337. package/tests/test_release_notes_generator.py +990 -0
  338. package/tests/test_sdk_loading.py +150 -0
  339. package/tests/test_skill_plugin_loading.py +149 -0
  340. package/tests/test_skills_have_readme.py +66 -0
  341. package/tests/test_sync_extensions.py +292 -0
  342. package/tests/test_workflow_sync.py +46 -0
  343. package/utils/analysis/README.md +7 -0
  344. package/utils/analysis/laminar_signals/README.md +211 -0
  345. package/utils/analysis/laminar_signals/analyze.py +780 -0
  346. package/utils/analysis/laminar_signals/templates/default.j2 +49 -0
  347. package/utils/analysis/laminar_signals/templates/pr_review.j2 +61 -0
@@ -0,0 +1,644 @@
1
+ # Custom Automation Reference
2
+
3
+ > **⚠️ Do NOT use this reference unless the user has explicitly requested a custom automation.** Always use the preset/prompt endpoint from the main SKILL.md first. If the preset approach cannot satisfy the requirement, explain the options to the user and let them decide.
4
+
5
+ This file contains detailed documentation for creating custom automations with user-provided code, uploads, and entrypoints.
6
+
7
+ **When to use custom automation (only if the user explicitly chooses this):**
8
+ - Full control over the automation code structure is needed
9
+ - Custom dependencies or a specific runtime are required
10
+ - The user has confirmed that the prompt preset does not meet their requirements
11
+
12
+ ## Table of Contents
13
+
14
+ 1. [Tarball Uploads](#uploading-a-tarball)
15
+ 2. [Creating Custom Automations](#creating-an-automation)
16
+ 3. [Managing Automations](#managing-automations)
17
+ 4. [Writing Automation Code](#writing-automation-code)
18
+ - [SDK-based Scripts](#sdk-based-scripts) — AI agent conversations
19
+ - [Deterministic Script (No LLM)](#deterministic-script-no-llm) — pure Python stdlib
20
+ 5. [Environment Variables](#environment-variables)
21
+ 6. [Validation Rules](#validation-rules)
22
+ 7. [Complete Examples](#complete-examples)
23
+
24
+ ---
25
+
26
+ ## Uploading a Tarball
27
+
28
+ Before creating a custom automation, you need to upload your code as a tarball. The upload endpoint streams directly to cloud storage with a **1MB size limit**.
29
+
30
+ ### Create a Tarball
31
+
32
+ ```bash
33
+ tar -czf automation.tar.gz -C /path/to/your/code .
34
+ ```
35
+
36
+ ### Tarball Structure
37
+
38
+ ```
39
+ automation.tar.gz
40
+ ├── main.py # Your entrypoint script
41
+ ├── setup.sh # Optional: install dependencies before entrypoint runs
42
+ └── requirements.txt # Optional: additional dependencies
43
+ ```
44
+
45
+ ### Validate Before Packaging
46
+
47
+ **Always validate syntax before creating the tarball.** This catches errors immediately and avoids uploading broken code that fails silently at runtime.
48
+
49
+ ```bash
50
+ python3 -m py_compile main.py # fails with a clear error on any syntax problem
51
+ bash -n setup.sh # validates shell syntax without executing
52
+ ```
53
+
54
+ Fix any errors reported before proceeding to the next step.
55
+
56
+
57
+ ### Upload the Tarball
58
+
59
+ First, determine the API host. Look for a `<HOST>` value in the system prompt. If present, use that URL. Otherwise, default to `https://app.all-hands.dev`.
60
+
61
+ Then upload:
62
+
63
+ ```bash
64
+ curl -X POST "${OPENHANDS_HOST}/api/automation/v1/uploads?name=my-automation&description=Weekly%20report%20generator" \
65
+ -H "Authorization: Bearer ${OPENHANDS_API_KEY}" \
66
+ -H "Content-Type: application/gzip" \
67
+ --data-binary @automation.tar.gz
68
+ ```
69
+
70
+ ### Upload Response
71
+
72
+ ```json
73
+ {
74
+ "id": "550e8400-e29b-41d4-a716-446655440000",
75
+ "tarball_path": "oh-internal://uploads/550e8400-e29b-41d4-a716-446655440000",
76
+ "status": "COMPLETED",
77
+ "size_bytes": 12345
78
+ }
79
+ ```
80
+
81
+ **Important:** Save the `tarball_path` value - you'll need it when creating the automation.
82
+
83
+ ### Upload Status Values
84
+
85
+ | Status | Description |
86
+ |--------|-------------|
87
+ | `UPLOADING` | Upload in progress |
88
+ | `COMPLETED` | Upload successful, `tarball_path` is available |
89
+ | `FAILED` | Upload failed, check `error_message` |
90
+
91
+ ---
92
+
93
+ ## Creating an Automation
94
+
95
+ Once you have a tarball uploaded (or an external URL), create the automation:
96
+
97
+ ```bash
98
+ curl -X POST "${OPENHANDS_HOST}/api/automation/v1" \
99
+ -H "Authorization: Bearer ${OPENHANDS_API_KEY}" \
100
+ -H "Content-Type: application/json" \
101
+ -d '{
102
+ "name": "Weekly Report Generator",
103
+ "trigger": {
104
+ "type": "cron",
105
+ "schedule": "0 9 * * 1",
106
+ "timezone": "UTC"
107
+ },
108
+ "tarball_path": "oh-internal://uploads/550e8400-e29b-41d4-a716-446655440000",
109
+ "entrypoint": "python main.py",
110
+ "timeout": 300
111
+ }'
112
+ ```
113
+
114
+ ### Request Fields
115
+
116
+ | Field | Required | Description |
117
+ |-------|----------|-------------|
118
+ | `name` | Yes | Name of the automation (1-500 characters) |
119
+ | `trigger.type` | Yes | Must be `"cron"` |
120
+ | `trigger.schedule` | Yes | Cron expression (5 fields: min hour day month weekday) |
121
+ | `trigger.timezone` | No | IANA timezone (default: `"UTC"`) |
122
+ | `tarball_path` | Yes | Path to code tarball (see Tarball Path Formats below) |
123
+ | `entrypoint` | Yes | Command to execute (e.g., `"python main.py"`, `"uv run script.py"`) |
124
+ | `setup_script_path` | No | Relative path to setup script inside tarball |
125
+ | `timeout` | No | Max execution time in seconds (1-600, default: 600) |
126
+
127
+ ### Tarball Path Formats
128
+
129
+ | Format | Example | Description |
130
+ |--------|---------|-------------|
131
+ | Internal upload | `oh-internal://uploads/{uuid}` | Uploaded via `/api/v1/uploads` |
132
+ | S3 | `s3://bucket/path/file.tar.gz` | AWS S3 bucket |
133
+ | GCS | `gs://bucket/path/file.tar.gz` | Google Cloud Storage |
134
+ | HTTPS | `https://example.com/file.tar.gz` | Public HTTPS URL |
135
+
136
+ ### Response (HTTP 201)
137
+
138
+ ```json
139
+ {
140
+ "id": "123e4567-e89b-12d3-a456-426614174000",
141
+ "name": "Weekly Report Generator",
142
+ "trigger": {
143
+ "type": "cron",
144
+ "schedule": "0 9 * * 1",
145
+ "timezone": "UTC"
146
+ },
147
+ "tarball_path": "oh-internal://uploads/550e8400-e29b-41d4-a716-446655440000",
148
+ "entrypoint": "python main.py",
149
+ "enabled": true,
150
+ "created_at": "2025-03-25T10:00:00Z"
151
+ }
152
+ ```
153
+
154
+ ---
155
+
156
+ ## Managing Automations
157
+
158
+ ### List Automations
159
+
160
+ ```bash
161
+ curl "${OPENHANDS_HOST}/api/automation/v1?limit=20" \
162
+ -H "Authorization: Bearer ${OPENHANDS_API_KEY}"
163
+ ```
164
+
165
+ ### Get Automation Details
166
+
167
+ ```bash
168
+ curl "${OPENHANDS_HOST}/api/automation/v1/{automation_id}" \
169
+ -H "Authorization: Bearer ${OPENHANDS_API_KEY}"
170
+ ```
171
+
172
+ ### Update Automation
173
+
174
+ ```bash
175
+ curl -X PATCH "${OPENHANDS_HOST}/api/automation/v1/{automation_id}" \
176
+ -H "Authorization: Bearer ${OPENHANDS_API_KEY}" \
177
+ -H "Content-Type: application/json" \
178
+ -d '{"enabled": false}'
179
+ ```
180
+
181
+ ### Delete Automation
182
+
183
+ ```bash
184
+ curl -X DELETE "${OPENHANDS_HOST}/api/automation/v1/{automation_id}" \
185
+ -H "Authorization: Bearer ${OPENHANDS_API_KEY}"
186
+ ```
187
+
188
+ ### Manually Trigger a Run
189
+
190
+ ```bash
191
+ curl -X POST "${OPENHANDS_HOST}/api/automation/v1/{automation_id}/dispatch" \
192
+ -H "Authorization: Bearer ${OPENHANDS_API_KEY}"
193
+ ```
194
+
195
+ ### List Automation Runs
196
+
197
+ ```bash
198
+ curl "${OPENHANDS_HOST}/api/automation/v1/{automation_id}/runs?limit=20" \
199
+ -H "Authorization: Bearer ${OPENHANDS_API_KEY}"
200
+ ```
201
+
202
+ **Run Status Values:**
203
+ | Status | Description |
204
+ |--------|-------------|
205
+ | `PENDING` | Run scheduled, waiting for dispatch |
206
+ | `RUNNING` | Execution in progress |
207
+ | `COMPLETED` | Run finished successfully |
208
+ | `FAILED` | Run failed, check `error_detail` |
209
+
210
+ ---
211
+
212
+ ## Writing Automation Code
213
+
214
+ ### How Execution Works
215
+
216
+ When a run is triggered, the automation service uploads your tarball to the agent server, which unpacks it, runs `setup.sh` to install dependencies, then executes your entrypoint. Your script therefore runs **inside the agent server** — not in a separate process.
217
+
218
+ The agent server exposes an HTTP API (at `AGENT_SERVER_URL`) for managing conversations. A **conversation** is an AI agent interaction that can use tools: bash commands, file editing, web browsing, and so on. Your script uses the SDK's `OpenHandsCloudWorkspace` (pointing to `AGENT_SERVER_URL`) to start, monitor, and stop conversations running in that same agent server.
219
+
220
+ Key points:
221
+ - **Your script and its conversations share the same agent server.** There is no network hop to a remote service.
222
+ - **Conversations are asynchronous.** You can fire one and continue, fire several concurrently, or start none at all (e.g. if your script fetches external data and decides no action is needed).
223
+ - **The completion callback** is sent by `OpenHandsCloudWorkspace.__exit__` when the `with` block exits, telling the automation service the run is done. For async patterns, defer exiting until the conversation is in the desired state.
224
+ - **Secrets** stored in the agent server are accessed via its REST API: `GET {AGENT_SERVER_URL}/api/settings/secrets/{name}` with `X-Session-API-Key: {SESSION_API_KEY}`. SDK scripts can also call `workspace.get_llm()` to get the configured LLM.
225
+
226
+ **SDK Documentation:** https://docs.openhands.dev/sdk
227
+
228
+ ### SDK-based Scripts
229
+
230
+ For scripts that start AI agent conversations, install the SDK packages in `setup.sh`:
231
+
232
+ ```bash
233
+ #!/bin/bash
234
+ set -e
235
+
236
+ # Install uv for fast dependency management (recommended)
237
+ curl -LsSf https://astral.sh/uv/install.sh | sh
238
+ export PATH="$HOME/.local/bin:$PATH"
239
+
240
+ # Install the OpenHands SDK packages from PyPI using uv
241
+ uv pip install -q openhands-sdk openhands-workspace openhands-tools
242
+ ```
243
+
244
+ ### Basic Automation Structure
245
+
246
+ ```python
247
+ """Example automation using the OpenHands SDK."""
248
+ import os
249
+
250
+ from openhands.sdk import Conversation
251
+ from openhands.tools.preset.default import get_default_agent
252
+ from openhands.workspace import OpenHandsCloudWorkspace
253
+
254
+ # AGENT_SERVER_URL is set by the automation service for the agent server URL.
255
+ # SESSION_API_KEY / OH_SESSION_API_KEYS_0 authenticate against that server.
256
+ api_key = os.environ.get("SESSION_API_KEY") or os.environ.get("OH_SESSION_API_KEYS_0", "")
257
+ api_url = os.environ.get("AGENT_SERVER_URL", "")
258
+
259
+ # OpenHandsCloudWorkspace connects back to the agent server to manage conversations.
260
+ # __exit__ sends the completion callback to the automation service.
261
+ with OpenHandsCloudWorkspace(
262
+ local_agent_server_mode=True,
263
+ cloud_api_url=api_url,
264
+ cloud_api_key=api_key,
265
+ ) as workspace:
266
+ llm = workspace.get_llm()
267
+ agent = get_default_agent(llm=llm, cli_mode=True)
268
+ conversation = Conversation(agent=agent, workspace=workspace)
269
+ conversation.send_message("Your automation prompt here")
270
+ conversation.run()
271
+ conversation.close()
272
+ # OpenHandsCloudWorkspace.__exit__ fires the completion callback here.
273
+ ```
274
+
275
+ ### Conversation Persistence
276
+
277
+ Conversations started during a run remain accessible in the OpenHands UI after the run completes — users can view the history and continue interacting. By default, `Conversation` does not delete the conversation on close:
278
+
279
+ ```python
280
+ # Default: conversation persists after close (users can view/continue it)
281
+ conversation = Conversation(agent=agent, workspace=workspace)
282
+
283
+ # Explicitly persist (same as default)
284
+ conversation = Conversation(agent=agent, workspace=workspace, delete_on_close=False)
285
+
286
+ # Delete conversation resources on close
287
+ conversation = Conversation(agent=agent, workspace=workspace, delete_on_close=True)
288
+ ```
289
+
290
+ The agent server itself persists until it times out or is manually deleted; this is managed by the automation service, not by the workspace.
291
+
292
+ ### Conversation Patterns
293
+
294
+ #### Pattern 1: Synchronous (run and wait)
295
+
296
+ The simplest pattern — start a conversation, block until it finishes, then exit (firing the callback).
297
+
298
+ ```python
299
+ conversation.send_message("Analyze the latest deployment logs and summarise any errors")
300
+ conversation.run() # blocks until the agent finishes or times out
301
+ conversation.close()
302
+ ```
303
+
304
+ #### Pattern 2: Conditional (fetch data first, then decide)
305
+
306
+ A common pattern where the script queries an external source and only starts a conversation if needed.
307
+
308
+ ```python
309
+ import httpx
310
+
311
+ response = httpx.get("https://api.example.com/alerts", headers={"Authorization": f"Bearer {token}"})
312
+ alerts = response.json().get("alerts", [])
313
+
314
+ if not alerts:
315
+ print("No alerts — nothing to do.")
316
+ else:
317
+ # Only now do we spin up an agent conversation
318
+ with OpenHandsCloudWorkspace(local_agent_server_mode=True, cloud_api_url=api_url, cloud_api_key=api_key) as workspace:
319
+ llm = workspace.get_llm()
320
+ agent = get_default_agent(llm=llm, cli_mode=True)
321
+ conversation = Conversation(agent=agent, workspace=workspace)
322
+ conversation.send_message(f"Investigate these alerts and open GitHub issues: {alerts}")
323
+ conversation.run()
324
+ conversation.close()
325
+ ```
326
+
327
+ #### Pattern 3: Wait for conversation completion (polling)
328
+
329
+ Start a conversation without blocking, do other work, then poll until the conversation reaches a terminal state before exiting. The callback fires only after the conversation is done.
330
+
331
+ `ConversationExecutionStatus.is_terminal()` returns `True` for `FINISHED`, `ERROR`, and `STUCK`. Call `refresh_from_server()` before checking status — `execution_status` uses a cached value and won't update automatically.
332
+
333
+ ```python
334
+ import time
335
+ from openhands.sdk.conversation.state import ConversationExecutionStatus
336
+
337
+ with OpenHandsCloudWorkspace(local_agent_server_mode=True, cloud_api_url=api_url, cloud_api_key=api_key) as workspace:
338
+ llm = workspace.get_llm()
339
+ agent = get_default_agent(llm=llm, cli_mode=True)
340
+ conversation = Conversation(agent=agent, workspace=workspace)
341
+ conversation.send_message("Run a long analysis task")
342
+ # Conversation is now running asynchronously in the agent server.
343
+
344
+ # Do other work here while conversation runs...
345
+
346
+ # Wait until the conversation reaches a terminal state.
347
+ while True:
348
+ time.sleep(5)
349
+ conversation.refresh_from_server()
350
+ if conversation.execution_status.is_terminal():
351
+ break
352
+
353
+ conversation.close()
354
+ # Callback fires here — after the conversation has finished.
355
+ ```
356
+
357
+ #### Pattern 4: Deferred callback via stop hook
358
+
359
+ For cases where the automation script needs to exit while a conversation is still running, use a `stop` hook to fire the completion callback from within the agent server when the conversation finishes.
360
+
361
+ The `stop` hook runs a shell command when the agent stops. The agent server's environment includes `AUTOMATION_CALLBACK_URL`, `AUTOMATION_CALLBACK_API_KEY`, and `AUTOMATION_RUN_ID`, so the hook can call the automation service directly.
362
+
363
+ ```python
364
+ from openhands.sdk.hooks import HookConfig, HookDefinition, HookMatcher
365
+
366
+ # Shell command that fires the completion callback when the agent stops.
367
+ # Runs inside the agent server — env vars are available at hook execution time.
368
+ stop_hook = HookConfig(
369
+ stop=[
370
+ HookMatcher(hooks=[
371
+ HookDefinition(
372
+ command=(
373
+ 'curl -sf -X POST "$AUTOMATION_CALLBACK_URL" '
374
+ '-H "Authorization: Bearer $AUTOMATION_CALLBACK_API_KEY" '
375
+ '-H "Content-Type: application/json" '
376
+ '-d \'{"status":"COMPLETED","run_id":"$AUTOMATION_RUN_ID"}\' || true'
377
+ )
378
+ )
379
+ ])
380
+ ]
381
+ )
382
+
383
+ with OpenHandsCloudWorkspace(local_agent_server_mode=True, cloud_api_url=api_url, cloud_api_key=api_key) as workspace:
384
+ llm = workspace.get_llm()
385
+ agent = get_default_agent(llm=llm, cli_mode=True)
386
+ conversation = Conversation(agent=agent, workspace=workspace, hook_config=stop_hook)
387
+ conversation.send_message("Do some long-running work")
388
+ # Don't call run() — the conversation runs asynchronously.
389
+ # When the agent stops, the stop hook will fire the callback.
390
+ # OpenHandsCloudWorkspace.__exit__ also fires a callback here (on script exit).
391
+ # The automation service should handle receiving two callbacks for the same run.
392
+ ```
393
+
394
+ > **Note:** When using the stop hook pattern, the automation service receives two completion callbacks — one from `OpenHandsCloudWorkspace.__exit__` when the script exits, and one from the stop hook when the conversation finishes. Ensure your automation service handles duplicate callbacks gracefully.
395
+
396
+ ---
397
+
398
+ ### Deterministic Script (No LLM)
399
+
400
+ For tasks that don't need AI reasoning — sending a Slack message, calling an API, rotating from a fixed list — skip the SDK entirely. Use pure Python stdlib with `python3 main.py` as the entrypoint and no `setup.sh`.
401
+
402
+ **Accessing secrets** — custom secrets are not injected into the subprocess environment automatically. Fetch them via the agent server's REST API:
403
+
404
+ ```python
405
+ import os, urllib.request
406
+
407
+ def get_secret(name: str) -> str:
408
+ url = os.environ.get("AGENT_SERVER_URL", "").rstrip("/")
409
+ key = os.environ.get("SESSION_API_KEY") or os.environ.get("OH_SESSION_API_KEYS_0", "")
410
+ req = urllib.request.Request(
411
+ f"{url}/api/settings/secrets/{name}",
412
+ headers={"X-Session-API-Key": key},
413
+ )
414
+ with urllib.request.urlopen(req) as r:
415
+ return r.read().decode().strip()
416
+ ```
417
+
418
+ **Firing the callback** — without the SDK, POST to `AUTOMATION_CALLBACK_URL` before exiting. If you never fire the callback the run stays `RUNNING` until the watchdog marks it `FAILED`.
419
+
420
+ ```python
421
+ import json, os, urllib.request
422
+
423
+ def fire_callback(status: str = "COMPLETED", error: str | None = None) -> None:
424
+ url = os.environ.get("AUTOMATION_CALLBACK_URL", "")
425
+ if not url:
426
+ return
427
+ body = {"status": status, "run_id": os.environ.get("AUTOMATION_RUN_ID", "")}
428
+ if error:
429
+ body["error"] = error
430
+ req = urllib.request.Request(url, data=json.dumps(body).encode(), headers={
431
+ "Content-Type": "application/json",
432
+ "Authorization": f"Bearer {os.environ.get('AUTOMATION_CALLBACK_API_KEY', '')}",
433
+ })
434
+ try:
435
+ urllib.request.urlopen(req)
436
+ except Exception as e:
437
+ print(f"Callback error (non-fatal): {e}")
438
+ ```
439
+
440
+ ---
441
+
442
+ ## Environment Variables
443
+
444
+ The automation service injects these environment variables into every run:
445
+
446
+ | Variable | Alt name | Description |
447
+ |----------|----------|-------------|
448
+ | `AGENT_SERVER_URL` | — | Agent server URL. Used as `cloud_api_url` for `OpenHandsCloudWorkspace`, and as the base URL for secret lookups |
449
+ | `OH_SESSION_API_KEYS_0` | `SESSION_API_KEY` | Session API key. Used as `cloud_api_key` for `OpenHandsCloudWorkspace`, and as `X-Session-API-Key` for REST API calls |
450
+ | `AUTOMATION_CALLBACK_URL` | — | POST here to mark the run complete (done automatically by `OpenHandsCloudWorkspace.__exit__`, or manually in no-LLM scripts) |
451
+ | `AUTOMATION_CALLBACK_API_KEY` | — | Bearer token for the completion callback POST |
452
+ | `AUTOMATION_RUN_ID` | — | Run ID to include in the completion callback payload |
453
+ | `AUTOMATION_EVENT_PAYLOAD` | — | JSON with trigger context: `automation_id`, `automation_name`, `trigger` type, and (for webhook runs) the raw event payload |
454
+
455
+ > **Note:** The session API key has two names: `SESSION_API_KEY` (cloud) and `OH_SESSION_API_KEYS_0` (local/dev). Always read both — see the code examples above.
456
+
457
+ ---
458
+
459
+ ## Validation Rules
460
+
461
+ - **Name**: 1-500 characters
462
+ - **Cron schedule**: Valid 5-field cron expression
463
+ - **Entrypoint**: Relative path, no shell metacharacters (`;`, `&`, `|`, etc.)
464
+ - **Setup script path**: Relative path, no path traversal (`..`)
465
+ - **Timeout**: 1-600 seconds (10 minutes max)
466
+ - **Tarball size**: 1MB max for uploads
467
+
468
+ ---
469
+
470
+ ## Complete Examples
471
+
472
+ ### No LLM (deterministic)
473
+
474
+ ```bash
475
+ OPENHANDS_HOST="https://app.all-hands.dev"
476
+ mkdir my-automation && cd my-automation
477
+
478
+ cat > main.py << 'PYEOF'
479
+ """Post a random quote to Slack — no LLM, no SDK."""
480
+ import json, os, random, sys, urllib.request
481
+
482
+ QUOTES = ["Stay hungry, stay foolish.", "Done is better than perfect."]
483
+ CHANNEL = "C12345678"
484
+
485
+ def get_secret(name):
486
+ url = os.environ.get("AGENT_SERVER_URL", "").rstrip("/")
487
+ key = os.environ.get("SESSION_API_KEY") or os.environ.get("OH_SESSION_API_KEYS_0", "")
488
+ req = urllib.request.Request(f"{url}/api/settings/secrets/{name}",
489
+ headers={"X-Session-API-Key": key})
490
+ with urllib.request.urlopen(req) as r:
491
+ return r.read().decode().strip()
492
+
493
+ def fire_callback(status="COMPLETED", error=None):
494
+ url = os.environ.get("AUTOMATION_CALLBACK_URL", "")
495
+ if not url: return
496
+ body = {"status": status, "run_id": os.environ.get("AUTOMATION_RUN_ID", "")}
497
+ if error: body["error"] = error
498
+ req = urllib.request.Request(url, data=json.dumps(body).encode(), headers={
499
+ "Content-Type": "application/json",
500
+ "Authorization": f"Bearer {os.environ.get('AUTOMATION_CALLBACK_API_KEY', '')}",
501
+ })
502
+ try: urllib.request.urlopen(req)
503
+ except Exception as e: print(f"Callback error: {e}")
504
+
505
+ try:
506
+ token = get_secret("SLACK_BOT_TOKEN")
507
+ msg = random.choice(QUOTES)
508
+ req = urllib.request.Request("https://slack.com/api/chat.postMessage",
509
+ data=json.dumps({"channel": CHANNEL, "text": msg}).encode(),
510
+ headers={"Content-Type": "application/json", "Authorization": f"Bearer {token}"})
511
+ result = json.loads(urllib.request.urlopen(req).read())
512
+ if not result.get("ok"): raise RuntimeError(result.get("error"))
513
+ print(f"Posted: {msg}")
514
+ fire_callback("COMPLETED")
515
+ except Exception as e:
516
+ print(f"ERROR: {e}", file=sys.stderr)
517
+ fire_callback("FAILED", str(e))
518
+ sys.exit(1)
519
+ PYEOF
520
+
521
+ tar -czf ../my-automation.tar.gz .
522
+
523
+ TARBALL_PATH=$(curl -s -X POST "${OPENHANDS_HOST}/api/automation/v1/uploads?name=my-automation" \
524
+ -H "Authorization: Bearer ${OPENHANDS_API_KEY}" \
525
+ -H "Content-Type: application/gzip" \
526
+ --data-binary @../my-automation.tar.gz | jq -r '.tarball_path')
527
+
528
+ curl -X POST "${OPENHANDS_HOST}/api/automation/v1" \
529
+ -H "Authorization: Bearer ${OPENHANDS_API_KEY}" \
530
+ -H "Content-Type: application/json" \
531
+ -d "{
532
+ \"name\": \"Daily Quote\",
533
+ \"trigger\": {\"type\": \"cron\", \"schedule\": \"0 9 * * *\", \"timezone\": \"UTC\"},
534
+ \"tarball_path\": \"$TARBALL_PATH\",
535
+ \"entrypoint\": \"python3 main.py\"
536
+ }"
537
+ ```
538
+
539
+ ### SDK Script (AI conversations)
540
+
541
+ ```bash
542
+ # 0. Set the API host (use value from <HOST> in system prompt, or default)
543
+ OPENHANDS_HOST="https://app.all-hands.dev"
544
+
545
+ # 1. Create your automation code
546
+ mkdir my-automation && cd my-automation
547
+
548
+ # Create setup.sh
549
+ cat > setup.sh << 'EOF'
550
+ #!/bin/bash
551
+ set -e
552
+ uv venv .venv --quiet
553
+ uv pip install --quiet \
554
+ openhands-sdk \
555
+ openhands-workspace \
556
+ openhands-tools
557
+ EOF
558
+ chmod +x setup.sh
559
+
560
+ # Create main.py using the SDK
561
+ cat > main.py << 'EOF'
562
+ """Weekly report automation using OpenHands SDK."""
563
+ import os
564
+ import json
565
+
566
+ from openhands.sdk import Conversation
567
+ from openhands.tools.preset.default import get_default_agent
568
+ from openhands.workspace import OpenHandsCloudWorkspace
569
+
570
+ payload = json.loads(os.environ.get('AUTOMATION_EVENT_PAYLOAD', '{}'))
571
+ print(f"Running: {payload.get('automation_name')}")
572
+
573
+ api_key = os.environ.get("SESSION_API_KEY") or os.environ.get("OH_SESSION_API_KEYS_0", "")
574
+ api_url = os.environ.get("AGENT_SERVER_URL", "")
575
+
576
+ with OpenHandsCloudWorkspace(
577
+ local_agent_server_mode=True,
578
+ cloud_api_url=api_url,
579
+ cloud_api_key=api_key,
580
+ ) as workspace:
581
+ llm = workspace.get_llm()
582
+ agent = get_default_agent(llm=llm, cli_mode=True)
583
+ conversation = Conversation(agent=agent, workspace=workspace)
584
+ conversation.send_message("Generate a weekly status report")
585
+ conversation.run()
586
+ conversation.close()
587
+
588
+ print("Automation completed!")
589
+ EOF
590
+
591
+ # 2. Validate syntax before packaging
592
+ python3 -m py_compile main.py
593
+ bash -n setup.sh
594
+
595
+ # 3. Create the tarball
596
+ tar -czf ../my-automation.tar.gz .
597
+
598
+ # 4. Upload the tarball
599
+ UPLOAD_RESPONSE=$(curl -s -X POST \
600
+ "${OPENHANDS_HOST}/api/automation/v1/uploads?name=my-automation" \
601
+ -H "Authorization: Bearer ${OPENHANDS_API_KEY}" \
602
+ -H "Content-Type: application/gzip" \
603
+ --data-binary @my-automation.tar.gz)
604
+
605
+ TARBALL_PATH=$(echo "$UPLOAD_RESPONSE" | jq -r '.tarball_path')
606
+
607
+ # 5. Create the automation
608
+ curl -X POST "${OPENHANDS_HOST}/api/automation/v1" \
609
+ -H "Authorization: Bearer ${OPENHANDS_API_KEY}" \
610
+ -H "Content-Type: application/json" \
611
+ -d "{
612
+ \"name\": \"Weekly Report Generator\",
613
+ \"trigger\": {\"type\": \"cron\", \"schedule\": \"0 9 * * 1\", \"timezone\": \"UTC\"},
614
+ \"tarball_path\": \"$TARBALL_PATH\",
615
+ \"entrypoint\": \".venv/bin/python main.py\",
616
+ \"setup_script_path\": \"setup.sh\",
617
+ \"timeout\": 300
618
+ }"
619
+ ```
620
+
621
+ ---
622
+
623
+ ## Troubleshooting
624
+
625
+ ### Upload Failed: File too large
626
+ The upload limit is 1MB. Reduce your tarball size by:
627
+ - Excluding unnecessary files
628
+ - Not including `node_modules`, `.venv`, or other dependency directories
629
+
630
+ ### Automation Not Running
631
+ 1. Check if the automation is enabled (`enabled: true`)
632
+ 2. Verify the cron schedule is correct
633
+ 3. Check for validation errors in the response
634
+
635
+ ### Run fails instantly with `error_detail: null`
636
+ The script sent `fire_callback("FAILED")` immediately — before doing meaningful work. Common causes:
637
+ - A required secret was empty: the `get_secret()` call failed or returned nothing
638
+ - A missing/wrong `AGENT_SERVER_URL` or `SESSION_API_KEY`
639
+ - An import error in the entrypoint
640
+
641
+ Add `"error": str(exc)` to your `fire_callback("FAILED", ...)` call so `error_detail` is populated.
642
+
643
+ ### Run stays `RUNNING` indefinitely, then fails
644
+ The completion callback was never fired. Every code path in your script must call `fire_callback()` — including exception handlers.
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "openhands-sdk",
3
+ "version": "1.0.0",
4
+ "description": "Reference skill for the OpenHands Software Agent SDK - build AI agents with custom tools, LLM configuration, conversations, sub-agent delegation, MCP integration, security, and persistence.",
5
+ "author": {
6
+ "name": "OpenHands",
7
+ "email": "contact@all-hands.dev"
8
+ },
9
+ "homepage": "https://github.com/OpenHands/extensions",
10
+ "repository": "https://github.com/OpenHands/extensions",
11
+ "license": "MIT",
12
+ "keywords": [
13
+ "sdk",
14
+ "agent",
15
+ "openhands",
16
+ "tools",
17
+ "llm",
18
+ "conversation"
19
+ ]
20
+ }
@@ -0,0 +1,22 @@
1
+ # openhands-sdk
2
+
3
+ Reference skill for the **OpenHands Software Agent SDK** - the Python framework
4
+ for building AI agents that write software.
5
+
6
+ This skill is a **thin pointer** to the canonical SDK documentation. All detailed
7
+ content lives on the docs site and is not duplicated here.
8
+
9
+ - Skill entry point: [`SKILL.md`](./SKILL.md)
10
+
11
+ ## Documentation
12
+
13
+ - [Full SDK docs](https://docs.openhands.dev/sdk)
14
+ - [LLMs.txt (structured doc index)](https://docs.openhands.dev/llms.txt)
15
+ - [SDK source code](https://github.com/OpenHands/software-agent-sdk)
16
+ - [Examples](https://github.com/OpenHands/software-agent-sdk/tree/main/examples/01_standalone_sdk)
17
+
18
+ ## Contributing SDK documentation
19
+
20
+ **Do not add SDK-specific documentation to this repo.** The source of truth is
21
+ [OpenHands/docs](https://github.com/OpenHands/docs). If you want to improve SDK
22
+ docs, submit changes there. This skill links directly to the docs site.