@galaxyproject/orbit 0.0.0 → 0.9.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 (451) hide show
  1. package/CHANGELOG.md +123 -0
  2. package/LICENSE +21 -0
  3. package/README.md +760 -5
  4. package/bin/legacy-mcp-config.js +363 -0
  5. package/bin/orbit.js +810 -0
  6. package/bin/pi-extension-path.d.ts +4 -0
  7. package/bin/pi-extension-path.js +44 -0
  8. package/bin/provider-auth.d.ts +22 -0
  9. package/bin/provider-auth.js +77 -0
  10. package/bin/thinking-pref.js +38 -0
  11. package/bin/update-check.d.ts +15 -0
  12. package/bin/update-check.js +137 -0
  13. package/extensions/cli-update/index.ts +32 -0
  14. package/extensions/orbit/activity-hooks.ts +141 -0
  15. package/extensions/orbit/activity.ts +116 -0
  16. package/extensions/orbit/agent-dir.ts +11 -0
  17. package/extensions/orbit/auto-resume.ts +352 -0
  18. package/extensions/orbit/config.ts +3 -0
  19. package/extensions/orbit/confusables.ts +1 -0
  20. package/extensions/orbit/context.ts +957 -0
  21. package/extensions/orbit/dashboard-commands.ts +191 -0
  22. package/extensions/orbit/dashboard-store.ts +473 -0
  23. package/extensions/orbit/dashboard-tools.ts +1185 -0
  24. package/extensions/orbit/empty-completion.ts +99 -0
  25. package/extensions/orbit/evidence-gate.ts +1276 -0
  26. package/extensions/orbit/evidence-override-command.ts +294 -0
  27. package/extensions/orbit/exec-guard/bash-risk.ts +1408 -0
  28. package/extensions/orbit/exec-guard/gate.ts +181 -0
  29. package/extensions/orbit/exec-guard/guardian-config.ts +54 -0
  30. package/extensions/orbit/exec-guard/index.ts +1 -0
  31. package/extensions/orbit/exec-guard/model-tier.ts +36 -0
  32. package/extensions/orbit/exec-guard/path-jail.ts +54 -0
  33. package/extensions/orbit/exec-guard/policy.ts +342 -0
  34. package/extensions/orbit/exec-guard/runtime-state.ts +15 -0
  35. package/extensions/orbit/exec-guard/sensitive-read.ts +525 -0
  36. package/extensions/orbit/exec-guard/types.ts +42 -0
  37. package/extensions/orbit/execution-commands.ts +134 -0
  38. package/extensions/orbit/feedback-command.ts +81 -0
  39. package/extensions/orbit/feedback.ts +96 -0
  40. package/extensions/orbit/galaxy-api.test.ts +122 -0
  41. package/extensions/orbit/galaxy-api.ts +606 -0
  42. package/extensions/orbit/galaxy-artifact-link-guidance.ts +5 -0
  43. package/extensions/orbit/galaxy-destructive-gate.ts +113 -0
  44. package/extensions/orbit/galaxy-enrich.ts +1108 -0
  45. package/extensions/orbit/galaxy-failure.ts +101 -0
  46. package/extensions/orbit/galaxy-fixture.ts +126 -0
  47. package/extensions/orbit/galaxy-history-guard.ts +233 -0
  48. package/extensions/orbit/galaxy-job-block.ts +347 -0
  49. package/extensions/orbit/galaxy-job-ops.ts +288 -0
  50. package/extensions/orbit/galaxy-live-source.ts +929 -0
  51. package/extensions/orbit/galaxy-markdown-adapter.ts +248 -0
  52. package/extensions/orbit/galaxy-op-policy.ts +332 -0
  53. package/extensions/orbit/galaxy-ops-tools.ts +326 -0
  54. package/extensions/orbit/galaxy-output-store.ts +80 -0
  55. package/extensions/orbit/galaxy-output.ts +346 -0
  56. package/extensions/orbit/galaxy-page-binding.ts +222 -0
  57. package/extensions/orbit/galaxy-page-markdown-guidance.ts +15 -0
  58. package/extensions/orbit/galaxy-page-ops.ts +159 -0
  59. package/extensions/orbit/galaxy-page-sync.test.ts +417 -0
  60. package/extensions/orbit/galaxy-page-sync.ts +318 -0
  61. package/extensions/orbit/galaxy-pages-api.ts +203 -0
  62. package/extensions/orbit/galaxy-pages-sync.ts +409 -0
  63. package/extensions/orbit/galaxy-poll-guard.ts +189 -0
  64. package/extensions/orbit/galaxy-poller.ts +706 -0
  65. package/extensions/orbit/galaxy-provenance.ts +623 -0
  66. package/extensions/orbit/galaxy-reconcile.ts +956 -0
  67. package/extensions/orbit/galaxy-recovery.ts +176 -0
  68. package/extensions/orbit/galaxy-repeat-guard.ts +55 -0
  69. package/extensions/orbit/galaxy-submission-capture.ts +707 -0
  70. package/extensions/orbit/galaxy-submission.ts +502 -0
  71. package/extensions/orbit/galaxy-tool-source.ts +78 -0
  72. package/extensions/orbit/galaxy-tool-wrap.ts +234 -0
  73. package/extensions/orbit/galaxy-udt-block.ts +127 -0
  74. package/extensions/orbit/galaxy-upload-tus.ts +334 -0
  75. package/extensions/orbit/galaxy-upload.ts +300 -0
  76. package/extensions/orbit/galaxy-version-capture.ts +65 -0
  77. package/extensions/orbit/git.ts +173 -0
  78. package/extensions/orbit/harness-block-fields.ts +597 -0
  79. package/extensions/orbit/index.ts +593 -0
  80. package/extensions/orbit/init-gate.ts +284 -0
  81. package/extensions/orbit/instructions-command.ts +136 -0
  82. package/extensions/orbit/invocation-failure-hint.ts +57 -0
  83. package/extensions/orbit/iwc-plan-check.ts +94 -0
  84. package/extensions/orbit/lesson-command.ts +303 -0
  85. package/extensions/orbit/lesson-hint.ts +245 -0
  86. package/extensions/orbit/lesson-nudge.ts +106 -0
  87. package/extensions/orbit/lessons/compose.ts +177 -0
  88. package/extensions/orbit/lessons/counters.ts +120 -0
  89. package/extensions/orbit/lessons/enabled.ts +116 -0
  90. package/extensions/orbit/lessons/matcher.ts +346 -0
  91. package/extensions/orbit/lessons/paths.ts +286 -0
  92. package/extensions/orbit/lessons/pi-event-contract.ts +105 -0
  93. package/extensions/orbit/lessons/propose-replay.ts +103 -0
  94. package/extensions/orbit/lessons/propose-tool.ts +222 -0
  95. package/extensions/orbit/lessons/propose.ts +502 -0
  96. package/extensions/orbit/lessons/replay.ts +89 -0
  97. package/extensions/orbit/lessons/reproduction-index.ts +83 -0
  98. package/extensions/orbit/lessons/search-tool.ts +99 -0
  99. package/extensions/orbit/lessons/search.ts +136 -0
  100. package/extensions/orbit/lessons/store.ts +415 -0
  101. package/extensions/orbit/lessons/text.ts +36 -0
  102. package/extensions/orbit/lessons/types.ts +69 -0
  103. package/extensions/orbit/lessons/user-dir.ts +24 -0
  104. package/extensions/orbit/lessons/user-lesson.ts +88 -0
  105. package/extensions/orbit/lessons/wrapper.ts +61 -0
  106. package/extensions/orbit/lessons-command.ts +147 -0
  107. package/extensions/orbit/local-exec.ts +39 -0
  108. package/extensions/orbit/mcp-output.ts +179 -0
  109. package/extensions/orbit/mcp-servers.test.ts +24 -0
  110. package/extensions/orbit/mcp-servers.ts +25 -0
  111. package/extensions/orbit/model-script.ts +351 -0
  112. package/extensions/orbit/notebook-anchors.ts +342 -0
  113. package/extensions/orbit/notebook-writer.ts +863 -0
  114. package/extensions/orbit/observation-allowlists.ts +1109 -0
  115. package/extensions/orbit/observation-replay.ts +148 -0
  116. package/extensions/orbit/observation-triggers.ts +563 -0
  117. package/extensions/orbit/observation-ui.ts +175 -0
  118. package/extensions/orbit/observations-command.ts +421 -0
  119. package/extensions/orbit/observations-config.ts +200 -0
  120. package/extensions/orbit/observations.ts +1381 -0
  121. package/extensions/orbit/profiles.ts +270 -0
  122. package/extensions/orbit/progress-updates.ts +95 -0
  123. package/extensions/orbit/proposal-commands.ts +882 -0
  124. package/extensions/orbit/provenance-fs-file.ts +59 -0
  125. package/extensions/orbit/provenance-fs.ts +32 -0
  126. package/extensions/orbit/provenance-memory-fs.ts +59 -0
  127. package/extensions/orbit/record-store-file.ts +177 -0
  128. package/extensions/orbit/record-store-page.ts +77 -0
  129. package/extensions/orbit/record-store-seam.ts +47 -0
  130. package/extensions/orbit/record-store.ts +149 -0
  131. package/extensions/orbit/registry-carrier.ts +107 -0
  132. package/extensions/orbit/registry-eligibility.ts +95 -0
  133. package/extensions/orbit/registry-evaluation.ts +403 -0
  134. package/extensions/orbit/registry-evaluator.ts +519 -0
  135. package/extensions/orbit/registry-import.ts +91 -0
  136. package/extensions/orbit/registry-lock.ts +221 -0
  137. package/extensions/orbit/registry-memory-fs.ts +42 -0
  138. package/extensions/orbit/registry-page-carrier.ts +103 -0
  139. package/extensions/orbit/registry-proposal.ts +1348 -0
  140. package/extensions/orbit/registry-runtime.ts +234 -0
  141. package/extensions/orbit/registry-schema.ts +735 -0
  142. package/extensions/orbit/registry-submitter.ts +59 -0
  143. package/extensions/orbit/registry-templates.ts +346 -0
  144. package/extensions/orbit/registry.ts +476 -0
  145. package/extensions/orbit/sandbox/index.ts +120 -0
  146. package/extensions/orbit/sandbox/sandbox-bash.ts +93 -0
  147. package/extensions/orbit/sandbox/sandbox-config.ts +87 -0
  148. package/extensions/orbit/sandbox/sandbox-init-message.ts +28 -0
  149. package/extensions/orbit/secret-redaction.ts +247 -0
  150. package/extensions/orbit/session-index/cwd.ts +18 -0
  151. package/extensions/orbit/session-index/db.ts +56 -0
  152. package/extensions/orbit/session-index/indexer.ts +154 -0
  153. package/extensions/orbit/session-index/is-enabled.ts +17 -0
  154. package/extensions/orbit/session-index/parse.ts +254 -0
  155. package/extensions/orbit/session-index/query.ts +209 -0
  156. package/extensions/orbit/session-index/schema.ts +72 -0
  157. package/extensions/orbit/session-index/tools.ts +152 -0
  158. package/extensions/orbit/session-lifecycle.ts +321 -0
  159. package/extensions/orbit/skill-triggers.ts +159 -0
  160. package/extensions/orbit/skills-command.ts +48 -0
  161. package/extensions/orbit/skills-discovery.ts +435 -0
  162. package/extensions/orbit/skills.ts +84 -0
  163. package/extensions/orbit/sra-import-gate.ts +77 -0
  164. package/extensions/orbit/state.ts +265 -0
  165. package/extensions/orbit/submission-replay.ts +147 -0
  166. package/extensions/orbit/sync-command.ts +157 -0
  167. package/extensions/orbit/teams/critic-parser.ts +93 -0
  168. package/extensions/orbit/teams/dispatcher.ts +210 -0
  169. package/extensions/orbit/teams/is-enabled.ts +22 -0
  170. package/extensions/orbit/teams/tool.ts +187 -0
  171. package/extensions/orbit/teams/types.ts +57 -0
  172. package/extensions/orbit/teams/validate.ts +42 -0
  173. package/extensions/orbit/tester-id-command.ts +133 -0
  174. package/extensions/orbit/tools-sync.ts +279 -0
  175. package/extensions/orbit/tools.ts +1300 -0
  176. package/extensions/orbit/types.ts +41 -0
  177. package/extensions/orbit/ui-bridge.ts +55 -0
  178. package/extensions/orbit/ulid.ts +60 -0
  179. package/extensions/orbit/user-instructions.ts +388 -0
  180. package/extensions/orbit/vendor/skills/_catalog.json +68 -0
  181. package/extensions/orbit/vendor/skills/_manifest.json +1106 -0
  182. package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/SKILL.md +112 -0
  183. package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/_provenance.json +218 -0
  184. package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/_required_tools.json +33 -0
  185. package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/_verify.json +4 -0
  186. package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/cli/add.json +50 -0
  187. package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/cli/draft-extract.json +51 -0
  188. package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/cli/draft-next-step.json +42 -0
  189. package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/cli/draft-validate.json +140 -0
  190. package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/cli/list.json +32 -0
  191. package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/notes/galaxy-tool-job-failure-reference.md +137 -0
  192. package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/notes/open-requirements-ledger.md +173 -0
  193. package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/schemas/galaxy-tool-summary.schema.json +175 -0
  194. package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/schemas/galaxy-workflow-draft.schema.json +2727 -0
  195. package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/SKILL.md +82 -0
  196. package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/_provenance.json +133 -0
  197. package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/_required_tools.json +16 -0
  198. package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/_verify.json +4 -0
  199. package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/references/cli/draft-validate.json +140 -0
  200. package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/references/notes/galaxy-workflow-draft-format.md +135 -0
  201. package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/references/notes/open-requirements-ledger.md +173 -0
  202. package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/references/schemas/galaxy-workflow-draft.schema.json +2727 -0
  203. package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/SKILL.md +84 -0
  204. package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/_provenance.json +166 -0
  205. package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/_verify.json +29 -0
  206. package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/notes/galaxy-workflow-testability-design.md +136 -0
  207. package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/notes/iwc-shortcuts-anti-patterns.md +357 -0
  208. package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/notes/iwc-test-data-conventions.md +308 -0
  209. package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/notes/planemo-asserts-idioms.md +246 -0
  210. package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/notes/planemo-workflow-test-architecture.md +151 -0
  211. package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/schemas/galaxy-workflow-test-plan.schema.json +552 -0
  212. package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/schemas/summary-galaxy-workflow.schema.json +687 -0
  213. package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/schemas/tests-format.schema.json +7328 -0
  214. package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/SKILL.md +127 -0
  215. package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/_provenance.json +198 -0
  216. package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/_required_tools.json +16 -0
  217. package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/_verify.json +4 -0
  218. package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/references/cli/convert.json +123 -0
  219. package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/references/notes/galaxy-data-flow-draft-contract.md +129 -0
  220. package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/references/notes/iwc-shortcuts-anti-patterns.md +357 -0
  221. package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/references/notes/iwc-test-data-conventions.md +308 -0
  222. package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/references/notes/open-requirements-ledger.md +173 -0
  223. package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/SKILL.md +73 -0
  224. package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/_provenance.json +160 -0
  225. package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/_verify.json +4 -0
  226. package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/galaxy-collection-semantics.md +43 -0
  227. package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/galaxy-collection-semantics.yml +1250 -0
  228. package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/galaxy-tool-job-failure-reference.md +137 -0
  229. package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/galaxy-workflow-invocation-failure-reference.md +138 -0
  230. package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/iwc-shortcuts-anti-patterns.md +357 -0
  231. package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/nextflow-operators-to-galaxy-collection-recipes.md +98 -0
  232. package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/planemo-asserts-idioms.md +246 -0
  233. package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/planemo-workflow-test-architecture.md +151 -0
  234. package/extensions/orbit/vendor/skills/discover-shed-tool/SKILL.md +174 -0
  235. package/extensions/orbit/vendor/skills/discover-shed-tool/_provenance.json +130 -0
  236. package/extensions/orbit/vendor/skills/discover-shed-tool/_required_tools.json +18 -0
  237. package/extensions/orbit/vendor/skills/discover-shed-tool/_verify.json +17 -0
  238. package/extensions/orbit/vendor/skills/discover-shed-tool/references/cli/tool-revisions.json +48 -0
  239. package/extensions/orbit/vendor/skills/discover-shed-tool/references/cli/tool-search.json +95 -0
  240. package/extensions/orbit/vendor/skills/discover-shed-tool/references/cli/tool-versions.json +39 -0
  241. package/extensions/orbit/vendor/skills/discover-shed-tool/references/notes/component-tool-shed-search.md +230 -0
  242. package/extensions/orbit/vendor/skills/discover-shed-tool/references/schemas/galaxy-tool-discovery.schema.json +231 -0
  243. package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/SKILL.md +93 -0
  244. package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/_provenance.json +201 -0
  245. package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/_required_tools.json +28 -0
  246. package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/_verify.json +17 -0
  247. package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/cli/planemo.md +44 -0
  248. package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/cli/validate-tests.json +40 -0
  249. package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/notes/galaxy-workflow-testability-design.md +136 -0
  250. package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/notes/iwc-shortcuts-anti-patterns.md +357 -0
  251. package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/notes/iwc-test-data-conventions.md +308 -0
  252. package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/notes/planemo-asserts-idioms.md +246 -0
  253. package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/notes/planemo-workflow-test-architecture.md +151 -0
  254. package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/schemas/galaxy-workflow-test-plan.schema.json +552 -0
  255. package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/schemas/tests-format.schema.json +7328 -0
  256. package/extensions/orbit/vendor/skills/interview-to-galaxy-workflow-changeset/SKILL.md +81 -0
  257. package/extensions/orbit/vendor/skills/interview-to-galaxy-workflow-changeset/_provenance.json +93 -0
  258. package/extensions/orbit/vendor/skills/interview-to-galaxy-workflow-changeset/_verify.json +17 -0
  259. package/extensions/orbit/vendor/skills/interview-to-galaxy-workflow-changeset/references/notes/open-requirements-ledger.md +173 -0
  260. package/extensions/orbit/vendor/skills/interview-to-galaxy-workflow-changeset/references/schemas/summary-galaxy-workflow.schema.json +687 -0
  261. package/extensions/orbit/vendor/skills/pipeline-update-interview-to-galaxy/SKILL.md +81 -0
  262. package/extensions/orbit/vendor/skills/pipeline-update-interview-to-galaxy/_assembly.json +23 -0
  263. package/extensions/orbit/vendor/skills/run-workflow-test/SKILL.md +79 -0
  264. package/extensions/orbit/vendor/skills/run-workflow-test/_provenance.json +125 -0
  265. package/extensions/orbit/vendor/skills/run-workflow-test/_required_tools.json +28 -0
  266. package/extensions/orbit/vendor/skills/run-workflow-test/_verify.json +4 -0
  267. package/extensions/orbit/vendor/skills/run-workflow-test/references/cli/planemo.md +44 -0
  268. package/extensions/orbit/vendor/skills/run-workflow-test/references/cli/validate-tests.json +40 -0
  269. package/extensions/orbit/vendor/skills/run-workflow-test/references/notes/galaxy-workflow-invocation-failure-reference.md +138 -0
  270. package/extensions/orbit/vendor/skills/run-workflow-test/references/notes/planemo-asserts-idioms.md +246 -0
  271. package/extensions/orbit/vendor/skills/run-workflow-test/references/notes/planemo-workflow-test-architecture.md +151 -0
  272. package/extensions/orbit/vendor/skills/run-workflow-test/references/schemas/tests-format.schema.json +7328 -0
  273. package/extensions/orbit/vendor/skills/skills/LICENSE +21 -0
  274. package/extensions/orbit/vendor/skills/skills/collection-manipulation/README.md +39 -0
  275. package/extensions/orbit/vendor/skills/skills/collection-manipulation/SKILL.md +892 -0
  276. package/extensions/orbit/vendor/skills/skills/collection-manipulation/references/api-patterns.md +927 -0
  277. package/extensions/orbit/vendor/skills/skills/collection-manipulation/references/apply-rules.md +1482 -0
  278. package/extensions/orbit/vendor/skills/skills/collection-manipulation/references/test-patterns.md +996 -0
  279. package/extensions/orbit/vendor/skills/skills/collection-manipulation/references/tools.md +609 -0
  280. package/extensions/orbit/vendor/skills/skills/galaxy-data-libraries/SKILL.md +170 -0
  281. package/extensions/orbit/vendor/skills/skills/galaxy-integration/README.md +162 -0
  282. package/extensions/orbit/vendor/skills/skills/galaxy-integration/SKILL.md +99 -0
  283. package/extensions/orbit/vendor/skills/skills/galaxy-integration/examples/README.md +119 -0
  284. package/extensions/orbit/vendor/skills/skills/galaxy-integration/examples/tool-checking.md +419 -0
  285. package/extensions/orbit/vendor/skills/skills/galaxy-integration/examples/workflow-testing.md +617 -0
  286. package/extensions/orbit/vendor/skills/skills/galaxy-integration/galaxy-integration.md +614 -0
  287. package/extensions/orbit/vendor/skills/skills/galaxy-integration/jupyterlite/SKILL.md +144 -0
  288. package/extensions/orbit/vendor/skills/skills/galaxy-integration/jupyterlite/examples/average_col3.ipynb +68 -0
  289. package/extensions/orbit/vendor/skills/skills/galaxy-integration/jupyterlite/examples/extract_sample_metadata.ipynb +190 -0
  290. package/extensions/orbit/vendor/skills/skills/galaxy-integration/jupyterlite/examples/variant_annotation.ipynb +816 -0
  291. package/extensions/orbit/vendor/skills/skills/galaxy-integration/jupyterlite/examples/vcp_variant_map.ipynb +443 -0
  292. package/extensions/orbit/vendor/skills/skills/galaxy-integration/scripts/README.md +165 -0
  293. package/extensions/orbit/vendor/skills/skills/galaxy-integration/scripts/galaxy_tool_checker.py +521 -0
  294. package/extensions/orbit/vendor/skills/skills/galaxy-mcp-reference/SKILL.md +128 -0
  295. package/extensions/orbit/vendor/skills/skills/galaxy-mcp-reference/gotchas.md +135 -0
  296. package/extensions/orbit/vendor/skills/skills/galaxy-mcp-reference/history-access.md +132 -0
  297. package/extensions/orbit/vendor/skills/skills/reproduciblify/SKILL.md +146 -0
  298. package/extensions/orbit/vendor/skills/skills/reproduciblify/references/directives.yml +496 -0
  299. package/extensions/orbit/vendor/skills/skills/udt-authoring/SKILL.md +245 -0
  300. package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/01-minimal-echo.yml +27 -0
  301. package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/02-single-data-in-out.yml +27 -0
  302. package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/03-multiple-data-js.yml +28 -0
  303. package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/04-select-integer-boolean.yml +41 -0
  304. package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/05-resource-requirements.yml +25 -0
  305. package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/06-biocontainer-tool.yml +38 -0
  306. package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/07-inline-python-script.yml +56 -0
  307. package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/08-configfile-script.yml +74 -0
  308. package/extensions/orbit/vendor/skills/skills/udt-authoring/references/common-mistakes.md +76 -0
  309. package/extensions/orbit/vendor/skills/skills/udt-authoring/references/schema-reference.md +155 -0
  310. package/extensions/orbit/vendor/skills/skills/udt-authoring/references/templating.md +196 -0
  311. package/extensions/orbit/vendor/skills/skills/udt-authoring/scripts/validate.py +109 -0
  312. package/extensions/orbit/vendor/skills/skills/workflow-reports/README.md +71 -0
  313. package/extensions/orbit/vendor/skills/skills/workflow-reports/SKILL.md +190 -0
  314. package/extensions/orbit/vendor/skills/skills/workflow-reports/examples/histology-staining.md +145 -0
  315. package/extensions/orbit/vendor/skills/skills/workflow-reports/examples/tissue-microarray-analysis.md +176 -0
  316. package/extensions/orbit/vendor/skills/skills/workflow-reports/references/directives.md +415 -0
  317. package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/SKILL.md +98 -0
  318. package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/_provenance.json +93 -0
  319. package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/_required_tools.json +28 -0
  320. package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/_verify.json +17 -0
  321. package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/references/cli/convert.json +123 -0
  322. package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/references/cli/foundry.md +25 -0
  323. package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/references/cli/validate.json +142 -0
  324. package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/references/schemas/summary-galaxy-workflow.schema.json +687 -0
  325. package/extensions/orbit/vendor/skills/validate-galaxy-workflow/SKILL.md +74 -0
  326. package/extensions/orbit/vendor/skills/validate-galaxy-workflow/_provenance.json +71 -0
  327. package/extensions/orbit/vendor/skills/validate-galaxy-workflow/_required_tools.json +16 -0
  328. package/extensions/orbit/vendor/skills/validate-galaxy-workflow/_verify.json +4 -0
  329. package/extensions/orbit/vendor/skills/validate-galaxy-workflow/references/cli/validate.json +142 -0
  330. package/extensions/orbit/vendor/skills/validate-galaxy-workflow/references/notes/galaxy-workflow-invocation-failure-reference.md +138 -0
  331. package/extensions/orbit/vendor/skills/validate-galaxy-workflow/references/notes/galaxy-workflow-testability-design.md +136 -0
  332. package/extensions/orbit/vendor-skills.ts +283 -0
  333. package/extensions/orbit/workspace-state-dir.ts +46 -0
  334. package/extensions/orbit-handoff/index.ts +73 -0
  335. package/extensions/orbit-handoff/orbit-launcher.ts +142 -0
  336. package/extensions/whats-new/index.ts +98 -0
  337. package/lessons/LICENSE +17 -0
  338. package/lessons/README.md +117 -0
  339. package/lessons/SCHEMA.md +187 -0
  340. package/lessons/build-snapshot.mjs +311 -0
  341. package/lessons/data/downloaded-file-is-not-what-its-extension-says.md +61 -0
  342. package/lessons/galaxy-api/403-history-is-a-hard-stop.md +57 -0
  343. package/lessons/galaxy-api/collection-into-single-dataset-input.md +61 -0
  344. package/lessons/galaxy-api/connectedvalue-in-command-line.md +62 -0
  345. package/lessons/galaxy-api/hid-is-not-an-id.md +59 -0
  346. package/lessons/galaxy-api/invoke-workflow-inputs-not-params.md +60 -0
  347. package/lessons/galaxy-api/repeat-param-pipe-keys.md +63 -0
  348. package/lessons/galaxy-api/run-tool-returns-on-submit.md +61 -0
  349. package/lessons/galaxy-tools/reference-index-not-on-server.md +62 -0
  350. package/lessons/log.md +42 -0
  351. package/lessons/reproduction/input-population-mismatch.md +59 -0
  352. package/lessons/reproduction/methods-text-vs-executed-parameters.md +62 -0
  353. package/lessons/snapshot.json +1004 -0
  354. package/lessons/stats/de-contrast-direction-and-sample-labels.md +63 -0
  355. package/lessons/stats/na-coerced-to-zero-in-filters.md +59 -0
  356. package/lessons/validate.mjs +1219 -0
  357. package/package.json +103 -5
  358. package/shared/agent-prompt.d.ts +8 -0
  359. package/shared/agent-prompt.js +15 -0
  360. package/shared/approval-prompt.d.ts +4 -0
  361. package/shared/approval-prompt.js +61 -0
  362. package/shared/brain-env.d.ts +14 -0
  363. package/shared/brain-env.js +108 -0
  364. package/shared/brain-exit.d.ts +20 -0
  365. package/shared/brain-exit.js +149 -0
  366. package/shared/capability-profile.d.ts +16 -0
  367. package/shared/capability-profile.js +75 -0
  368. package/shared/config-source.d.ts +13 -0
  369. package/shared/config-source.js +17 -0
  370. package/shared/confusables.d.ts +3 -0
  371. package/shared/confusables.js +93 -0
  372. package/shared/custom-provider.d.ts +45 -0
  373. package/shared/custom-provider.js +169 -0
  374. package/shared/dashboard-contract.d.ts +123 -0
  375. package/shared/dashboard-contract.js +560 -0
  376. package/shared/dashboard-layout-store.d.ts +44 -0
  377. package/shared/dashboard-layout-store.js +281 -0
  378. package/shared/feedback-contract.d.ts +61 -0
  379. package/shared/feedback-contract.js +146 -0
  380. package/shared/galaxy-artifact-links.d.ts +30 -0
  381. package/shared/galaxy-artifact-links.js +166 -0
  382. package/shared/galaxy-credentials.d.ts +26 -0
  383. package/shared/galaxy-credentials.js +71 -0
  384. package/shared/galaxy-destructive.d.ts +21 -0
  385. package/shared/galaxy-destructive.js +269 -0
  386. package/shared/galaxy-guidance.d.ts +2 -0
  387. package/shared/galaxy-guidance.js +53 -0
  388. package/shared/galaxy-live-contract.d.ts +92 -0
  389. package/shared/galaxy-live-contract.js +180 -0
  390. package/shared/galaxy-page-cells.d.ts +2 -0
  391. package/shared/galaxy-page-cells.js +22 -0
  392. package/shared/galaxy-tool-family.d.ts +5 -0
  393. package/shared/galaxy-tool-family.js +27 -0
  394. package/shared/galaxy-url.d.ts +1 -0
  395. package/shared/galaxy-url.js +48 -0
  396. package/shared/gtn.d.ts +31 -0
  397. package/shared/gtn.js +253 -0
  398. package/shared/handled-prompt.d.ts +3 -0
  399. package/shared/handled-prompt.js +26 -0
  400. package/shared/known-provider.d.ts +11 -0
  401. package/shared/known-provider.js +89 -0
  402. package/shared/lesson-hint-marker.d.ts +7 -0
  403. package/shared/lesson-hint-marker.js +32 -0
  404. package/shared/lesson-rules.d.ts +150 -0
  405. package/shared/lesson-rules.js +203 -0
  406. package/shared/malformed-object-ids.d.ts +1 -0
  407. package/shared/malformed-object-ids.js +37 -0
  408. package/shared/markdown-sections.d.ts +10 -0
  409. package/shared/markdown-sections.js +118 -0
  410. package/shared/notebook-fences.d.ts +12 -0
  411. package/shared/notebook-fences.js +53 -0
  412. package/shared/observation-contract.d.ts +91 -0
  413. package/shared/observation-contract.js +544 -0
  414. package/shared/orbit-config.d.ts +226 -0
  415. package/shared/orbit-config.js +189 -0
  416. package/shared/orbit-env.d.ts +13 -0
  417. package/shared/orbit-env.js +124 -0
  418. package/shared/orbit-shell-contract.d.ts +74 -0
  419. package/shared/orbit-shell-contract.js +30 -0
  420. package/shared/page-content-hash.d.ts +1 -0
  421. package/shared/page-content-hash.js +13 -0
  422. package/shared/pi-default-model.d.ts +7 -0
  423. package/shared/pi-default-model.js +41 -0
  424. package/shared/prompt-blocks.d.ts +87 -0
  425. package/shared/prompt-blocks.js +898 -0
  426. package/shared/provider-auth-caps.d.ts +22 -0
  427. package/shared/provider-auth-caps.js +57 -0
  428. package/shared/random.d.ts +2 -0
  429. package/shared/random.js +15 -0
  430. package/shared/record-excerpt.d.ts +4 -0
  431. package/shared/record-excerpt.js +16 -0
  432. package/shared/redirect-guard.d.ts +36 -0
  433. package/shared/redirect-guard.js +235 -0
  434. package/shared/skills-pin.d.ts +8 -0
  435. package/shared/skills-pin.js +9 -0
  436. package/shared/skills-router.d.ts +17 -0
  437. package/shared/skills-router.js +63 -0
  438. package/shared/sra-import.d.ts +34 -0
  439. package/shared/sra-import.js +206 -0
  440. package/shared/state-dir.d.ts +21 -0
  441. package/shared/state-dir.js +240 -0
  442. package/shared/team-dispatch-contract.d.ts +28 -0
  443. package/shared/team-dispatch-contract.js +1 -0
  444. package/shared/truncate-middle.d.ts +11 -0
  445. package/shared/truncate-middle.js +68 -0
  446. package/shared/version-compare.d.ts +10 -0
  447. package/shared/version-compare.js +74 -0
  448. package/shared/whats-new.d.ts +31 -0
  449. package/shared/whats-new.js +83 -0
  450. package/shared/wsl.d.ts +10 -0
  451. package/shared/wsl.js +24 -0
@@ -0,0 +1,234 @@
1
+ /**
2
+ * What pi's MCP client gave every Galaxy tool for free, rebuilt for calls that run in-process:
3
+ * a deadline, a spill for oversized output, and a result the guards can classify without reading
4
+ * its prose.
5
+ *
6
+ * Every result is a galaxy-ops envelope (`{data, success, message, count, pagination}`, the
7
+ * Python server's shape key for key), including the ones built here -- no connection, a refused
8
+ * argument, the deadline -- so everything downstream parses one shape. The model reads the
9
+ * envelope as JSON text; `structuredContent` carries the same object for programmatic callers;
10
+ * `details.galaxy` says which tool ran, how it failed, and where a spilled output went.
11
+ */
12
+ import type { AgentToolResult } from "@earendil-works/pi-coding-agent";
13
+ import type { GalaxyContext, GalaxyResult } from "@galaxyproject/galaxy-ops";
14
+ import { utf8ByteLength } from "../../shared/truncate-middle.js";
15
+ import { galaxyOpsContext } from "./galaxy-api";
16
+ import { GALAXY_DEADLINE_TEXT, kindOfEnvelope, type GalaxyCallKind } from "./galaxy-failure";
17
+ import { record, spillPreview } from "./galaxy-output";
18
+ import { galaxyOutputStore } from "./galaxy-output-store";
19
+ import type { GalaxyTool } from "./galaxy-tool-source";
20
+
21
+ /** pi's MCP client gave every request five minutes; so does every in-process op. */
22
+ export const GALAXY_DEADLINE_MS = 300_000;
23
+ /** Above this many UTF-8 bytes the model gets a bounded preview and the full text is saved. */
24
+ export const GALAXY_SPILL_BYTES = 20 * 1024;
25
+
26
+ export type GalaxyEnvelope = GalaxyResult<unknown>;
27
+
28
+ export interface GalaxyCallDetails {
29
+ /** `kind` is set on every failure, null when the failure has no recovery advice. */
30
+ galaxy: { tool: string; kind?: GalaxyCallKind | null; outputId?: string; elapsedMs: number };
31
+ }
32
+
33
+ export interface GalaxyCallResult {
34
+ content: { type: "text"; text: string }[];
35
+ structuredContent: GalaxyEnvelope;
36
+ details: GalaxyCallDetails;
37
+ isError: boolean;
38
+ }
39
+
40
+ export type GalaxyContextFactory = (signal: AbortSignal) => GalaxyContext | null;
41
+
42
+ export interface GalaxyCallOptions {
43
+ deadlineMs?: number;
44
+ /** Where the call's context comes from; tests hand in their own. */
45
+ context?: GalaxyContextFactory;
46
+ }
47
+
48
+ /** A failure envelope in runWithEnvelope's own shape, for a refusal made before Galaxy was asked. */
49
+ export function galaxyFailure(message: string, errorKind?: string): GalaxyEnvelope {
50
+ return {
51
+ data: undefined,
52
+ success: false,
53
+ message,
54
+ ...(errorKind === undefined ? {} : { errorKind: errorKind as GalaxyEnvelope["errorKind"] }),
55
+ };
56
+ }
57
+
58
+ const NOT_CONNECTED =
59
+ "Galaxy is not connected: no Galaxy URL and API key are configured. Ask the user to run /connect.";
60
+
61
+ /** The envelope as the model and the guards see it, spilled when it is too large to show whole. */
62
+ export function renderGalaxyResult(
63
+ tool: string,
64
+ env: GalaxyEnvelope,
65
+ elapsedMs: number,
66
+ kind?: GalaxyCallKind,
67
+ ): GalaxyCallResult {
68
+ let text = JSON.stringify(env);
69
+ let outputId: string | undefined;
70
+ if (utf8ByteLength(text) > GALAXY_SPILL_BYTES) {
71
+ outputId = galaxyOutputStore().put(text, tool);
72
+ text = spillPreview(text, outputId, env.success === false);
73
+ }
74
+ return {
75
+ content: [{ type: "text", text }],
76
+ structuredContent: env,
77
+ details: {
78
+ galaxy: {
79
+ tool,
80
+ ...(env.success === false ? { kind: kind ?? null } : {}),
81
+ ...(outputId === undefined ? {} : { outputId }),
82
+ elapsedMs,
83
+ },
84
+ },
85
+ isError: env.success === false,
86
+ };
87
+ }
88
+
89
+ type Deadlined<T> = { timedOut: false; value: T; late: boolean } | { timedOut: true };
90
+
91
+ /**
92
+ * Run `work` against a deadline, and return when the deadline fires even if `work` never does.
93
+ *
94
+ * Joining the timer into the signal cancels well-behaved requests and poll sleeps, but a
95
+ * transport is free to ignore its signal, and openapi-fetch then waits forever; only racing the
96
+ * timer guarantees the call comes back. Our own timer rather than `AbortSignal.timeout` so fake
97
+ * timers can drive it. A caller's abort is rethrown: that is pi stopping the turn, not a failure.
98
+ */
99
+ async function withDeadline<T>(
100
+ signal: AbortSignal | undefined,
101
+ deadlineMs: number,
102
+ work: (joined: AbortSignal) => Promise<T>,
103
+ ): Promise<Deadlined<T>> {
104
+ const timer = new AbortController();
105
+ const handle = setTimeout(() => timer.abort(), deadlineMs);
106
+ const joined = signal ? AbortSignal.any([signal, timer.signal]) : timer.signal;
107
+ let unlisten = () => {};
108
+ const aborted = new Promise<{ kind: "aborted" }>((resolve) => {
109
+ const on = () => resolve({ kind: "aborted" });
110
+ if (joined.aborted) return on();
111
+ joined.addEventListener("abort", on, { once: true });
112
+ unlisten = () => joined.removeEventListener("abort", on);
113
+ });
114
+ try {
115
+ let running: Promise<T>;
116
+ try {
117
+ running = work(joined);
118
+ } catch (error) {
119
+ running = Promise.reject(error);
120
+ }
121
+ // Both outcomes are handled here, so the losing call's late rejection is never unhandled.
122
+ const settled = await Promise.race([
123
+ running.then(
124
+ (value) => ({ kind: "value" as const, value }),
125
+ (error: unknown) => ({ kind: "error" as const, error }),
126
+ ),
127
+ aborted,
128
+ ]);
129
+ if (signal?.aborted) throw signal.reason;
130
+ if (settled.kind === "value") {
131
+ return { timedOut: false, value: settled.value, late: timer.signal.aborted };
132
+ }
133
+ // A call that throws once the deadline fired (a poll sleep's plain Error("aborted"), an
134
+ // aborted fetch) failed because of the deadline, whatever it threw.
135
+ if (settled.kind === "aborted" || timer.signal.aborted) return { timedOut: true };
136
+ throw settled.error;
137
+ } finally {
138
+ clearTimeout(handle);
139
+ unlisten();
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Run one Galaxy call under the deadline, with a context bound to the joined signal, and render
145
+ * what comes back. `call` returns an envelope; anything it throws that is not the deadline's
146
+ * doing is rethrown, and pi turns it into an error result.
147
+ */
148
+ export async function runGalaxyCall(
149
+ tool: string,
150
+ signal: AbortSignal | undefined,
151
+ call: (ctx: GalaxyContext, signal: AbortSignal) => Promise<GalaxyEnvelope>,
152
+ opts: GalaxyCallOptions = {},
153
+ ): Promise<GalaxyCallResult> {
154
+ const started = Date.now();
155
+ const contextFor = opts.context ?? galaxyOpsContext;
156
+ const outcome = await withDeadline(
157
+ signal,
158
+ opts.deadlineMs ?? GALAXY_DEADLINE_MS,
159
+ async (joined): Promise<{ env: GalaxyEnvelope; kind?: GalaxyCallKind }> => {
160
+ const ctx = contextFor(joined);
161
+ if (!ctx) return { env: galaxyFailure(NOT_CONNECTED, "auth"), kind: "auth" };
162
+ return { env: await call(ctx, joined) };
163
+ },
164
+ );
165
+ const elapsedMs = Date.now() - started;
166
+ if (outcome.timedOut) {
167
+ return renderGalaxyResult(tool, galaxyFailure(GALAXY_DEADLINE_TEXT), elapsedMs, "timeout");
168
+ }
169
+ const { env, kind } = outcome.value;
170
+ if (env.success !== false) return renderGalaxyResult(tool, env, elapsedMs);
171
+ // A failure that lands after the timer fired is the aborted request's, not Galaxy's answer.
172
+ if (outcome.late) {
173
+ return renderGalaxyResult(tool, galaxyFailure(GALAXY_DEADLINE_TEXT), elapsedMs, "timeout");
174
+ }
175
+ return renderGalaxyResult(tool, env, elapsedMs, kind ?? kindOfEnvelope(env.errorKind));
176
+ }
177
+
178
+ /**
179
+ * The same deadline and spill for a tool a shell supplies itself (an extra), so its large output
180
+ * is read back through `galaxy_read_output` and the guards know it as a Galaxy call. A shell opts
181
+ * an extra in; the source never wraps one on its own.
182
+ */
183
+ export function wrapGalaxyTool(def: GalaxyTool, opts: { deadlineMs?: number } = {}): GalaxyTool {
184
+ return {
185
+ ...def,
186
+ async execute(toolCallId, params, signal, onUpdate, ctx) {
187
+ const started = Date.now();
188
+ const outcome = await withDeadline(signal, opts.deadlineMs ?? GALAXY_DEADLINE_MS, (joined) =>
189
+ Promise.resolve(def.execute(toolCallId, params, joined, onUpdate, ctx)),
190
+ );
191
+ const elapsedMs = Date.now() - started;
192
+ if (outcome.timedOut) {
193
+ return {
194
+ content: [{ type: "text", text: GALAXY_DEADLINE_TEXT }],
195
+ details: { galaxy: { tool: def.name, kind: "timeout", elapsedMs } },
196
+ isError: true,
197
+ };
198
+ }
199
+ const result = outcome.value as AgentToolResult<unknown>;
200
+ const text = result.content.flatMap((c) => (c.type === "text" ? [c.text] : [])).join("\n");
201
+ let outputId: string | undefined;
202
+ let content = result.content;
203
+ if (utf8ByteLength(text) > GALAXY_SPILL_BYTES) {
204
+ outputId = galaxyOutputStore().put(text, def.name);
205
+ content = [
206
+ ...result.content.filter((c) => c.type !== "text"),
207
+ { type: "text", text: spillPreview(text, outputId, result.isError === true) },
208
+ ];
209
+ }
210
+ return {
211
+ ...result,
212
+ content,
213
+ details: {
214
+ ...record(result.details),
215
+ galaxy: { tool: def.name, ...(outputId === undefined ? {} : { outputId }), elapsedMs },
216
+ },
217
+ };
218
+ },
219
+ };
220
+ }
221
+
222
+ /** `outputSchema` for every in-process Galaxy tool: the envelope, as runWithEnvelope builds it. */
223
+ export const GALAXY_ENVELOPE_SCHEMA = Object.freeze({
224
+ type: "object",
225
+ properties: {
226
+ data: {},
227
+ success: { type: "boolean" },
228
+ message: { type: "string" },
229
+ count: { type: ["integer", "null"] },
230
+ pagination: { type: ["object", "null"] },
231
+ errorKind: { type: "string" },
232
+ },
233
+ required: ["success"],
234
+ });
@@ -0,0 +1,127 @@
1
+ /**
2
+ * `loom-udt` notebook blocks: a pointer to a user-defined tool's definition.
3
+ *
4
+ * A UDT lives in Galaxy's database, scoped to the user who made it. That is
5
+ * fine until the analysis outlives the account, the server, or the tool --
6
+ * at which point the notebook says a tool ran and nothing says what it was.
7
+ * So when the harness sees a UDT created, it writes the definition Galaxy
8
+ * stored to `<analysis>/.loom/provenance/udt/<tool_id>.yaml` and drops this
9
+ * block in the notebook pointing at it.
10
+ *
11
+ * Keyed on `tool_uuid`, which is what `run_user_tool` takes, so a run can be
12
+ * traced back to the exact definition it used. Re-creating the same tool id
13
+ * yields a new uuid and therefore a new block, which is correct: a redefined
14
+ * tool is a different tool.
15
+ *
16
+ * ```loom-udt
17
+ * tool_id: clean_table
18
+ * tool_uuid: 8d5f1c2e-...
19
+ * definition: .loom/provenance/udt/clean_table.yaml
20
+ * created_at: 2026-09-16T15:30:00Z
21
+ * notebook_anchor: plan-a-step-2
22
+ * attempt_id: 01K5CJ6XWQ8QK4S2M7E9V0TZ3B
23
+ * ```
24
+ */
25
+
26
+ export interface UdtYaml {
27
+ toolId: string;
28
+ toolUuid: string;
29
+ /** Repo-relative path to the stored definition. */
30
+ definition: string;
31
+ createdAt: string;
32
+ notebookAnchor: string;
33
+ attemptId?: string;
34
+ }
35
+
36
+ import { notebookFenceOpen } from "../../shared/notebook-fences.js";
37
+ import { appendBlock, isUnambiguousRange } from "./notebook-writer";
38
+ import { scanFencedBlocks } from "./harness-block-fields";
39
+
40
+ const UDT_FENCE_OPEN = notebookFenceOpen("udt");
41
+ const UDT_FENCE_CLOSE = "```";
42
+
43
+ function escapeYaml(value: string): string {
44
+ if (value === "") return '""';
45
+ if (/^[\w .\-/]+$/.test(value)) return value;
46
+ return JSON.stringify(value);
47
+ }
48
+
49
+ function unescapeYaml(value: string): string {
50
+ const trimmed = value.trim();
51
+ if (trimmed.startsWith('"')) {
52
+ try {
53
+ return JSON.parse(trimmed) as string;
54
+ } catch {
55
+ return trimmed;
56
+ }
57
+ }
58
+ return trimmed;
59
+ }
60
+
61
+ export function renderUdtYaml(udt: UdtYaml): string {
62
+ const lines: string[] = [
63
+ UDT_FENCE_OPEN,
64
+ `tool_id: ${escapeYaml(udt.toolId)}`,
65
+ `tool_uuid: ${escapeYaml(udt.toolUuid)}`,
66
+ `definition: ${escapeYaml(udt.definition)}`,
67
+ `created_at: ${udt.createdAt}`,
68
+ `notebook_anchor: ${udt.notebookAnchor}`,
69
+ ];
70
+ if (udt.attemptId) lines.push(`attempt_id: ${udt.attemptId}`);
71
+ lines.push(UDT_FENCE_CLOSE);
72
+ return lines.join("\n") + "\n";
73
+ }
74
+
75
+ function parseUdtBlock(blockLines: string[]): UdtYaml | null {
76
+ const map = new Map<string, string>();
77
+ for (const line of blockLines) {
78
+ const idx = line.indexOf(":");
79
+ if (idx === -1) continue;
80
+ map.set(line.slice(0, idx).trim(), line.slice(idx + 1).trim());
81
+ }
82
+ const toolUuid = map.get("tool_uuid");
83
+ const toolId = map.get("tool_id");
84
+ const definition = map.get("definition");
85
+ if (!toolUuid || !toolId || !definition) return null;
86
+ return {
87
+ toolId: unescapeYaml(toolId),
88
+ toolUuid: unescapeYaml(toolUuid),
89
+ definition: unescapeYaml(definition),
90
+ createdAt: map.get("created_at") ?? "",
91
+ notebookAnchor: map.get("notebook_anchor") ?? "",
92
+ attemptId: map.get("attempt_id") || undefined,
93
+ };
94
+ }
95
+
96
+ export function findUdtBlocks(content: string): UdtYaml[] {
97
+ const lines = content.split("\n");
98
+ const out: UdtYaml[] = [];
99
+ for (const range of scanFencedBlocks(lines, "udt")) {
100
+ const parsed = parseUdtBlock(lines.slice(range.start + 1, range.end));
101
+ if (parsed) out.push(parsed);
102
+ }
103
+ return out;
104
+ }
105
+
106
+ /** True when a `loom-udt` block for this uuid is already in the notebook. */
107
+ export function hasUdtBlock(content: string, toolUuid: string): boolean {
108
+ return findUdtBlocks(content).some((b) => b.toolUuid === toolUuid);
109
+ }
110
+
111
+ /** Upsert a `loom-udt` block keyed by `tool_uuid`. */
112
+ export function upsertUdtBlock(content: string, udt: UdtYaml): string {
113
+ const lines = content.split("\n");
114
+ const newBlock = renderUdtYaml(udt).trimEnd().split("\n");
115
+
116
+ for (const range of scanFencedBlocks(lines, "udt")) {
117
+ if (!isUnambiguousRange(lines, range.start)) continue;
118
+ const parsed = parseUdtBlock(lines.slice(range.start + 1, range.end));
119
+ if (parsed && parsed.toolUuid === udt.toolUuid) {
120
+ return [...lines.slice(0, range.start), ...newBlock, ...lines.slice(range.end + 1)].join(
121
+ "\n",
122
+ );
123
+ }
124
+ }
125
+
126
+ return appendBlock(content, newBlock);
127
+ }
@@ -0,0 +1,334 @@
1
+ /*
2
+ * Native TUS upload core for Galaxy.
3
+ * This will migrate to galaxy-ops when that lands -- temporary home here.
4
+ *
5
+ * Intentionally free of any loom/pi imports so it can move cleanly into
6
+ * @galaxyproject/galaxy-ops later. Plain Node + tus-js-client only.
7
+ */
8
+
9
+ import { createReadStream } from "fs";
10
+ import { parse as parseLegacyUrl } from "url";
11
+ // FileUrlStorage is exported by the Node build but missing from the type defs --
12
+ // import via namespace and cast to avoid the spurious TS2724 error.
13
+ import * as tusClient from "tus-js-client";
14
+ const { Upload, FileUrlStorage } = tusClient as typeof tusClient & {
15
+ FileUrlStorage: new (path: string) => tusClient.UrlStorage;
16
+ };
17
+
18
+ // ---------------------------------------------------------------------------
19
+ // buildFetchPayload
20
+ // ---------------------------------------------------------------------------
21
+
22
+ export interface FetchPayloadOpts {
23
+ historyId: string;
24
+ sessionId: string;
25
+ fileName: string;
26
+ fileType?: string; // Galaxy ext; default "auto"
27
+ dbkey?: string; // genome build; default "?"
28
+ }
29
+
30
+ /**
31
+ * Body for POST /api/tools/fetch after a TUS upload completes. Mirrors bioblend's
32
+ * _fetch_payload. auto_decompress is OFF so an uploaded .gz stays compressed.
33
+ */
34
+ export function buildFetchPayload(o: FetchPayloadOpts): Record<string, unknown> {
35
+ return {
36
+ history_id: o.historyId,
37
+ targets: [
38
+ {
39
+ destination: { type: "hdas" },
40
+ elements: [
41
+ {
42
+ src: "files",
43
+ ext: o.fileType ?? "auto",
44
+ dbkey: o.dbkey ?? "?",
45
+ to_posix_lines: true,
46
+ space_to_tab: false,
47
+ name: o.fileName,
48
+ },
49
+ ],
50
+ },
51
+ ],
52
+ "files_0|file_data": { session_id: o.sessionId, name: o.fileName },
53
+ auto_decompress: false,
54
+ };
55
+ }
56
+
57
+ // ---------------------------------------------------------------------------
58
+ // tusUpload
59
+ // ---------------------------------------------------------------------------
60
+
61
+ export interface TusUploadOpts {
62
+ baseUrl: string; // origin, no trailing slash, no /api (e.g. https://galaxy.test)
63
+ apiKey: string;
64
+ filePath: string; // already validated + realpath-resolved by the caller
65
+ storagePath: string; // file-backed resume store, e.g. ~/.loom/upload-resume.json
66
+ chunkSize?: number;
67
+ signal?: AbortSignal;
68
+ onProgress?: (bytesSent: number, bytesTotal: number) => void;
69
+ }
70
+
71
+ export interface TusUploadResult {
72
+ sessionId: string;
73
+ }
74
+
75
+ // Streams require a finite chunk size -- 10 MiB is a safe default that keeps
76
+ // memory bounded while still being large enough to avoid excessive round-trips.
77
+ const DEFAULT_CHUNK = 10 * 1024 * 1024;
78
+
79
+ /**
80
+ * Scheme + host + port, or null when the URL will not parse. Duplicated rather
81
+ * than imported from `shared/` on purpose: this module's header keeps it free
82
+ * of loom imports so it can move into @galaxyproject/galaxy-ops unchanged.
83
+ */
84
+ function originOf(url: string | null | undefined): string | null {
85
+ try {
86
+ return new URL(String(url)).origin;
87
+ } catch {
88
+ return null;
89
+ }
90
+ }
91
+
92
+ const DEFAULT_PORTS: Record<string, string> = { "http:": "80", "https:": "443" };
93
+
94
+ /**
95
+ * The origin tus-js-client will actually connect to. Its Node transport builds
96
+ * request options with the legacy `url.parse`, which disagrees with WHATWG
97
+ * `URL` on some hosts: `https://usegalaxy.org%2eau/x` is `usegalaxy.org.au`
98
+ * to `URL` but `usegalaxy.org` to `url.parse`. Checking only the WHATWG origin
99
+ * would pass that URL and then send the key to the other host.
100
+ */
101
+ function transportOriginOf(url: string | null | undefined): string | null {
102
+ try {
103
+ const { protocol, hostname, port } = parseLegacyUrl(String(url));
104
+ if (!protocol || !hostname) return null;
105
+ const effectivePort = port && port !== DEFAULT_PORTS[protocol] ? `:${port}` : "";
106
+ // url.parse drops the brackets from an IPv6 literal; the WHATWG origin keeps them.
107
+ const host = hostname.includes(":") ? `[${hostname}]` : hostname.toLowerCase();
108
+ return `${protocol}//${host}${effectivePort}`;
109
+ } catch {
110
+ return null;
111
+ }
112
+ }
113
+
114
+ /** A stored resume entry. `uploadUrl` is real at runtime; the type defs omit it. */
115
+ type StoredUpload = tusClient.PreviousUpload & {
116
+ uploadUrl?: string | null;
117
+ urlStorageKey?: string;
118
+ };
119
+
120
+ /**
121
+ * The resume store with anything off-origin filtered out, in both directions.
122
+ *
123
+ * tus writes the upload URL to the store right after it hands it to us, and
124
+ * abort() does not cancel that write; FileUrlStorage.addUpload keys every entry
125
+ * on a fresh random id, so it appends rather than overwrites and nothing ever
126
+ * removes it. A refused foreign URL would therefore be persisted to
127
+ * ~/.loom/upload-resume.json once per attempt, unbounded, and the first entry
128
+ * back out would be the poisoned one -- killing resume for that file for good.
129
+ *
130
+ * Filtering on the way in keeps it out of the file; filtering on the way out
131
+ * (and deleting what it finds) heals a file an earlier build already wrote.
132
+ */
133
+ function originScopedUrlStorage(
134
+ inner: tusClient.UrlStorage,
135
+ belongsHere: (url: string | null | undefined) => boolean,
136
+ ): tusClient.UrlStorage {
137
+ const keep = async (found: tusClient.PreviousUpload[]): Promise<tusClient.PreviousUpload[]> => {
138
+ const kept: tusClient.PreviousUpload[] = [];
139
+ for (const entry of found as StoredUpload[]) {
140
+ if (belongsHere(entry.uploadUrl)) {
141
+ kept.push(entry);
142
+ } else if (entry.urlStorageKey) {
143
+ // Best effort: a store we cannot prune still yields the right result,
144
+ // it just stays dirty.
145
+ await inner.removeUpload(entry.urlStorageKey).catch(() => {});
146
+ }
147
+ }
148
+ return kept;
149
+ };
150
+ return {
151
+ findAllUploads: async () => keep(await inner.findAllUploads()),
152
+ findUploadsByFingerprint: async (fingerprint: string) =>
153
+ keep(await inner.findUploadsByFingerprint(fingerprint)),
154
+ removeUpload: (urlStorageKey: string) => inner.removeUpload(urlStorageKey),
155
+ // Returning "" instead of a key is what tells tus there is nothing to
156
+ // remove later (_removeFromUrlStorage bails on a falsy key).
157
+ addUpload: async (fingerprint: string, upload: tusClient.PreviousUpload) =>
158
+ belongsHere((upload as StoredUpload).uploadUrl) ? inner.addUpload(fingerprint, upload) : "",
159
+ };
160
+ }
161
+
162
+ export function tusUpload(opts: TusUploadOpts): Promise<TusUploadResult> {
163
+ return new Promise<TusUploadResult>((resolve, reject) => {
164
+ // TUS hands back the upload URL in a Location header, and every later PATCH
165
+ // carries `x-api-key`. A Location pointing at another host would therefore
166
+ // send the key there, which is the same exposure an HTTP redirect would
167
+ // give us -- so the upload URL has to stay on the configured origin.
168
+ const expectedOrigin = originOf(opts.baseUrl);
169
+ // Stream and finish() are created unconditionally so cleanup is always
170
+ // the same code path regardless of when abort is detected.
171
+ const stream = createReadStream(opts.filePath);
172
+ let settled = false;
173
+
174
+ const finish = (fn: () => void) => {
175
+ if (settled) return;
176
+ settled = true;
177
+ opts.signal?.removeEventListener("abort", onAbort);
178
+ stream.destroy();
179
+ fn();
180
+ };
181
+
182
+ // Pre-flight abort: signal was already aborted before we even started.
183
+ // Route through finish() so stream cleanup is invariant.
184
+ if (opts.signal?.aborted) {
185
+ finish(() => reject(abortError()));
186
+ return;
187
+ }
188
+
189
+ const upload = new Upload(stream as unknown as File, {
190
+ endpoint: `${opts.baseUrl}/api/upload/resumable_upload`,
191
+ headers: { "x-api-key": opts.apiKey },
192
+ chunkSize: opts.chunkSize ?? DEFAULT_CHUNK,
193
+ retryDelays: [0, 1000, 3000, 5000],
194
+ urlStorage: originScopedUrlStorage(new FileUrlStorage(opts.storagePath), (url) =>
195
+ sameOriginAsGalaxy(url),
196
+ ),
197
+ storeFingerprintForResuming: true,
198
+ removeFingerprintOnSuccess: true,
199
+ onProgress: opts.onProgress,
200
+ onUploadUrlAvailable: () => {
201
+ if (sameOriginAsGalaxy(upload.url)) return;
202
+ // abort() without terminate: a DELETE would send the key to the very
203
+ // origin we are refusing to talk to. tus checks the aborted flag before
204
+ // the next PATCH, so nothing further goes out.
205
+ void upload.abort();
206
+ finish(() => reject(new Error(foreignUploadUrlMessage(upload.url))));
207
+ },
208
+ onError: (err: Error | tusClient.DetailedError) => finish(() => reject(err)),
209
+ onSuccess: () => {
210
+ // Use the URL constructor to parse the upload URL so trailing slashes
211
+ // and query strings don't yield the wrong segment.
212
+ let sessionId: string | undefined;
213
+ try {
214
+ const parsed = new URL(upload.url ?? "");
215
+ sessionId = parsed.pathname.split("/").filter(Boolean).pop();
216
+ } catch {
217
+ // malformed or empty url -- sessionId stays undefined
218
+ }
219
+ finish(() =>
220
+ sessionId
221
+ ? resolve({ sessionId })
222
+ : reject(new Error("TUS upload completed but Galaxy returned no session id")),
223
+ );
224
+ },
225
+ });
226
+
227
+ function sameOriginAsGalaxy(url: string | null | undefined): boolean {
228
+ const actual = originOf(url);
229
+ return (
230
+ actual !== null && actual === expectedOrigin && transportOriginOf(url) === expectedOrigin
231
+ );
232
+ }
233
+
234
+ function foreignUploadUrlMessage(url: string | null | undefined): string {
235
+ // The origin only: an upload URL's path is a session token.
236
+ const target = originOf(url) ?? "an address that could not be read";
237
+ return (
238
+ `Galaxy directed the upload to ${target}, which is a different origin than the ` +
239
+ `configured ${expectedOrigin ?? opts.baseUrl}. The upload was refused and the API key ` +
240
+ `was not sent to it. Check that GALAXY_URL points at the Galaxy server itself rather ` +
241
+ `than a proxy or a sign-in page.`
242
+ );
243
+ }
244
+
245
+ function onAbort() {
246
+ // No shouldTerminate: true -- leaving the partial TUS session on the server
247
+ // so it can be resumed if the user retries the same file (tus-js-client
248
+ // matches by fingerprint). Terminating here would force a full re-upload.
249
+ // If abort fires during the findPreviousUploads() window (before start()),
250
+ // abort() is a safe no-op; the settled guard below is what skips start().
251
+ void upload.abort();
252
+ finish(() => reject(abortError()));
253
+ }
254
+
255
+ opts.signal?.addEventListener("abort", onAbort, { once: true });
256
+
257
+ // tus-js-client only STORES resume state on its own -- it does not resume on
258
+ // start(). Look up a stored partial for this same file and continue it;
259
+ // otherwise start fresh. If the stored session is gone server-side (404),
260
+ // tus-js-client drops the stale entry and creates a new upload itself (we
261
+ // always set `endpoint`), so a stale resume transparently restarts.
262
+ upload
263
+ .findPreviousUploads()
264
+ .then((previous) => {
265
+ if (settled) return; // aborted while the resume lookup was in flight
266
+ // Everything here is already on the configured origin: the store
267
+ // wrapper drops (and deletes) anything else before we see it.
268
+ if (previous.length > 0) upload.resumeFromPreviousUpload(previous[0]);
269
+ upload.start();
270
+ })
271
+ .catch(() => {
272
+ // A failed resume lookup (e.g. an unreadable store) must not strand the
273
+ // upload -- fall back to a fresh start.
274
+ if (!settled) upload.start();
275
+ });
276
+ });
277
+ }
278
+
279
+ function abortError(): Error {
280
+ return new DOMException("Upload aborted", "AbortError");
281
+ }
282
+
283
+ // ---------------------------------------------------------------------------
284
+ // waitForDataset
285
+ // ---------------------------------------------------------------------------
286
+
287
+ export interface DatasetState {
288
+ id: string;
289
+ state: string;
290
+ hid?: number;
291
+ name?: string;
292
+ }
293
+
294
+ // Galaxy's terminal dataset states -- those from which no further transitions
295
+ // occur, so polling stops (successfully or otherwise) once one is seen. Mirrors
296
+ // Dataset.terminal_states in galaxy's lib/galaxy/model/__init__.py. "empty"
297
+ // (a successfully-uploaded but zero-byte dataset) belongs here too -- omitting
298
+ // it left an empty-file upload polling all the way to the ingest timeout.
299
+ export const TERMINAL_DATASET_STATES = new Set([
300
+ "ok",
301
+ "empty",
302
+ "error",
303
+ "discarded",
304
+ "failed_metadata",
305
+ "deferred",
306
+ ]);
307
+
308
+ export interface WaitOpts {
309
+ get: (datasetId: string) => Promise<DatasetState>;
310
+ intervalMs?: number;
311
+ timeoutMs?: number;
312
+ now?: () => number; // injectable for tests
313
+ }
314
+
315
+ export async function waitForDataset(datasetId: string, opts: WaitOpts): Promise<DatasetState> {
316
+ const interval = opts.intervalMs ?? 1500;
317
+ const timeout = opts.timeoutMs ?? 600_000;
318
+ const now = opts.now ?? (() => Date.now());
319
+ const start = now();
320
+
321
+ // Timeout bounds time-between-terminal-states, not absolute wall time including
322
+ // individual get() calls. A hung get() is the caller's responsibility -- the
323
+ // caller passes an abort-aware getter, so an aborted get() rejects and breaks
324
+ // the loop.
325
+ while (true) {
326
+ const ds = await opts.get(datasetId);
327
+ if (TERMINAL_DATASET_STATES.has(ds.state)) return ds;
328
+ if (now() - start > timeout)
329
+ throw new Error(
330
+ `Upload ingest timed out after ${Math.round(timeout / 1000)}s (last state: ${ds.state})`,
331
+ );
332
+ await new Promise((r) => setTimeout(r, interval));
333
+ }
334
+ }