@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,281 @@
1
+ /**
2
+ * The layout file, read and written the same way by everything that touches it.
3
+ *
4
+ * Three processes write `.loom-dashboard.json`: the Electron main process on the
5
+ * renderer's behalf, the web server on the browser's behalf, and the brain. Each
6
+ * had its own copy of "lstat, read, compare the revision, stage a temp file,
7
+ * rename", and hand-mirrored copies drift -- one of them followed a dangling
8
+ * symlink the others refused, and none of them was a compare-and-swap.
9
+ *
10
+ * It was not a compare-and-swap because the check and the rename were separated
11
+ * by an `await`. Two saves based on the same revision both passed the check and
12
+ * both reported success, and the second silently replaced the first: reproduced
13
+ * three times out of three against a real directory. Serializing per path inside
14
+ * the process and re-checking immediately before the rename closes that.
15
+ *
16
+ * **The promise chain serializes one process.** The shell and the brain are two,
17
+ * and two processes that both pass the revision check on the same base both
18
+ * rename, both report success, and the later rename silently discards the
19
+ * earlier write -- reproduced with two instances of this module. So the lock is
20
+ * also a lock FILE beside the layout, created with O_EXCL and held for the
21
+ * critical section; every writer in every process goes through `withLayoutLock`,
22
+ * so the file is the one thing they all agree on. A lock left behind by a
23
+ * process that died is taken over once it is stale.
24
+ */
25
+
26
+ import { randomBytes } from "node:crypto";
27
+ import * as fs from "node:fs";
28
+ import * as fsp from "node:fs/promises";
29
+ import * as path from "node:path";
30
+
31
+ import { dashboardRevision } from "./dashboard-contract.js";
32
+
33
+ /**
34
+ * One promise chain per absolute path. Every save on a path queues behind the
35
+ * previous one, so the read-check-rename below is a critical section rather
36
+ * than three syscalls that happen to be near each other.
37
+ */
38
+ const chains = new Map();
39
+
40
+ /**
41
+ * How long a lock file may sit before another process takes it over. The
42
+ * critical section is a few small reads and one rename, so anything holding it
43
+ * this long is gone; the risk of taking over a live one is a lost write, which
44
+ * is what the lock exists to prevent, so this errs long.
45
+ */
46
+ const LOCK_STALE_MS = 10_000;
47
+ /** How long a writer waits for the lock before giving up. */
48
+ const LOCK_WAIT_MS = 5_000;
49
+ const LOCK_RETRY_MS = 20;
50
+
51
+ function lockPathFor(absPath) {
52
+ return `${absPath}.lock`;
53
+ }
54
+
55
+ function sleep(ms) {
56
+ return new Promise((resolve) => setTimeout(resolve, ms));
57
+ }
58
+
59
+ /**
60
+ * Take the lock file, or throw once the wait runs out.
61
+ *
62
+ * `wx` is O_CREAT | O_EXCL: the create either makes the file or fails, and it
63
+ * fails on a symlink at that name too, so a planted link cannot stand in for
64
+ * the lock. A stale file -- by its own mtime -- is removed and the create
65
+ * retried; `rm` on a symlink removes the link, never what it points at.
66
+ */
67
+ async function acquireLockFile(lockPath) {
68
+ const deadline = Date.now() + LOCK_WAIT_MS;
69
+ for (;;) {
70
+ try {
71
+ const fd = await fsp.open(lockPath, "wx");
72
+ await fd.close();
73
+ return;
74
+ } catch (err) {
75
+ if (err?.code !== "EEXIST") throw err;
76
+ }
77
+ try {
78
+ const st = await fsp.lstat(lockPath);
79
+ if (Date.now() - st.mtimeMs > LOCK_STALE_MS) {
80
+ await fsp.rm(lockPath, { force: true });
81
+ continue;
82
+ }
83
+ } catch {
84
+ // Gone between the failed create and the lstat: the holder released it.
85
+ continue;
86
+ }
87
+ if (Date.now() >= deadline) {
88
+ throw new Error("another process is writing the dashboard layout; try again");
89
+ }
90
+ await sleep(LOCK_RETRY_MS);
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Run `fn` with nothing else on this path running at the same time -- in this
96
+ * process, by the promise chain, and in any other, by the lock file.
97
+ */
98
+ export function withLayoutLock(absPath, fn) {
99
+ const key = path.resolve(absPath);
100
+ const previous = chains.get(key) ?? Promise.resolve();
101
+ const lockPath = lockPathFor(key);
102
+ const locked = async () => {
103
+ await acquireLockFile(lockPath);
104
+ try {
105
+ return await fn();
106
+ } finally {
107
+ await fsp.rm(lockPath, { force: true }).catch(() => {});
108
+ }
109
+ };
110
+ // Settle either way: one caller's failure must not wedge the queue behind it.
111
+ const run = previous.then(locked, locked);
112
+ const settled = run.then(
113
+ () => {},
114
+ () => {},
115
+ );
116
+ chains.set(key, settled);
117
+ // Drop the entry once nothing is waiting, so a long session switching
118
+ // analysis directories does not accumulate one chain per directory.
119
+ void settled.then(() => {
120
+ if (chains.get(key) === settled) chains.delete(key);
121
+ });
122
+ return run;
123
+ }
124
+
125
+ /** For tests: is anything queued? */
126
+ export function layoutLockIdle() {
127
+ return chains.size === 0;
128
+ }
129
+
130
+ /**
131
+ * Read the layout file, through a descriptor.
132
+ *
133
+ * `lstat` first, and on the link itself rather than its target: a dangling
134
+ * symlink has to read as "there is a symlink here", not as "there is no file
135
+ * here". The web shell used `existsSync`, which follows the link and therefore
136
+ * called a dangling one absent -- so it then replaced it while the desktop
137
+ * refused, which is the drift this module exists to end.
138
+ *
139
+ * Then the open, with O_NOFOLLOW, and everything after it on the descriptor:
140
+ * the agent can write in this directory, and a symlink swapped in between the
141
+ * `lstat` and a read by name is followed by the read. Reviewers won that race
142
+ * against the plain `readFile` this used to be, and the bytes of whatever the
143
+ * link pointed at came back down the layout channel. The kernel refuses the
144
+ * link at open time; the `fstat` after it has to agree with the `lstat` before
145
+ * it on identity, regular-file-ness and size; and the read itself stops at the
146
+ * cap plus one byte, so a file that grows after the `fstat` is refused rather
147
+ * than served. On Windows there is no O_NOFOLLOW and the identity check is what
148
+ * remains.
149
+ */
150
+ export async function readLayoutFile(absPath, maxBytes) {
151
+ let stat;
152
+ try {
153
+ stat = await fsp.lstat(absPath);
154
+ } catch (err) {
155
+ if (err?.code === "ENOENT") return { ok: true, raw: null, revision: null };
156
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
157
+ }
158
+ const name = path.basename(absPath);
159
+ if (stat.isSymbolicLink()) {
160
+ return { ok: false, error: `${name} is a symlink; refusing to read` };
161
+ }
162
+ if (!stat.isFile()) {
163
+ return { ok: false, error: `${name} is not a regular file` };
164
+ }
165
+ // Checked before the open, so an oversized file is never pulled into memory --
166
+ // it used to come back whole inside a conflict response.
167
+ if (stat.size > maxBytes) {
168
+ return { ok: false, error: `dashboard layout is larger than ${maxBytes} bytes` };
169
+ }
170
+
171
+ let fd;
172
+ try {
173
+ fd = await fsp.open(absPath, fs.constants.O_RDONLY | (fs.constants.O_NOFOLLOW ?? 0));
174
+ } catch (err) {
175
+ // Removed between the two: that is "no file", the same as before the lstat.
176
+ if (err?.code === "ENOENT") return { ok: true, raw: null, revision: null };
177
+ // ELOOP is a symlink swapped in after the lstat: the race, refused.
178
+ if (err?.code === "ELOOP")
179
+ return { ok: false, error: `${name} is a symlink; refusing to read` };
180
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
181
+ }
182
+ try {
183
+ const st = await fd.stat();
184
+ if (!st.isFile()) return { ok: false, error: `${name} is not a regular file` };
185
+ if (!sameIdentity(stat, st)) {
186
+ return { ok: false, error: `${name} changed while it was being read` };
187
+ }
188
+ if (st.size > maxBytes) {
189
+ return { ok: false, error: `dashboard layout is larger than ${maxBytes} bytes` };
190
+ }
191
+ const buf = Buffer.alloc(maxBytes + 1);
192
+ let total = 0;
193
+ while (total < buf.length) {
194
+ const { bytesRead } = await fd.read(buf, total, buf.length - total, total);
195
+ if (bytesRead === 0) break;
196
+ total += bytesRead;
197
+ }
198
+ if (total > maxBytes) {
199
+ return { ok: false, error: `dashboard layout is larger than ${maxBytes} bytes` };
200
+ }
201
+ const raw = buf.subarray(0, total).toString("utf8");
202
+ return { ok: true, raw, revision: dashboardRevision(raw) };
203
+ } catch (err) {
204
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
205
+ } finally {
206
+ await fd.close().catch(() => {});
207
+ }
208
+ }
209
+
210
+ /**
211
+ * Is the descriptor the file the lstat saw? Inode and device settle it where
212
+ * the platform reports them; a filesystem that reports zero for either gets no
213
+ * check, which is the Windows fallback rather than a refusal of every read.
214
+ */
215
+ function sameIdentity(a, b) {
216
+ if (!a.ino || !b.ino) return true;
217
+ return a.ino === b.ino && a.dev === b.dev;
218
+ }
219
+
220
+ /**
221
+ * Replace the layout file if it still has the revision the caller based its
222
+ * change on.
223
+ *
224
+ * `baseRevision === undefined` means an unconditional write -- the escape hatch
225
+ * for a reset over a file nothing can parse. `null` means "there should be no
226
+ * file yet" and is checked like any other revision.
227
+ */
228
+ export async function casWriteLayoutFile(absPath, raw, baseRevision, maxBytes) {
229
+ if (typeof raw !== "string") return { ok: false, error: "expected dashboard JSON text" };
230
+ if (Buffer.byteLength(raw, "utf8") > maxBytes) {
231
+ return { ok: false, error: `dashboard layout is larger than ${maxBytes} bytes` };
232
+ }
233
+
234
+ try {
235
+ return await withLayoutLock(absPath, async () => {
236
+ const current = await readLayoutFile(absPath, maxBytes);
237
+ if (!current.ok) return current;
238
+ if (baseRevision !== undefined && baseRevision !== current.revision) {
239
+ return {
240
+ ok: false,
241
+ conflict: true,
242
+ error: "the dashboard changed on disk since it was loaded",
243
+ raw: current.raw,
244
+ revision: current.revision,
245
+ };
246
+ }
247
+
248
+ // Stage first, then re-check, then rename with nothing awaited in between.
249
+ // The scratch name is random and the write is `wx` (O_CREAT | O_EXCL) for
250
+ // the same reason the real name is lstat'd: the agent can write in this
251
+ // directory, and a guessable scratch name is a second place to plant a
252
+ // symlink that the following write would go through.
253
+ const tmp = `${absPath}.tmp.${randomBytes(8).toString("hex")}`;
254
+ try {
255
+ await fsp.writeFile(tmp, raw, { encoding: "utf8", flag: "wx" });
256
+ const before = await readLayoutFile(absPath, maxBytes);
257
+ if (!before.ok) {
258
+ await fsp.rm(tmp, { force: true });
259
+ return before;
260
+ }
261
+ if (baseRevision !== undefined && baseRevision !== before.revision) {
262
+ await fsp.rm(tmp, { force: true });
263
+ return {
264
+ ok: false,
265
+ conflict: true,
266
+ error: "the dashboard changed on disk since it was loaded",
267
+ raw: before.raw,
268
+ revision: before.revision,
269
+ };
270
+ }
271
+ await fsp.rename(tmp, absPath);
272
+ return { ok: true, revision: dashboardRevision(raw) };
273
+ } catch (err) {
274
+ await fsp.rm(tmp, { force: true }).catch(() => {});
275
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
276
+ }
277
+ });
278
+ } catch (err) {
279
+ return { ok: false, error: err instanceof Error ? err.message : String(err) };
280
+ }
281
+ }
@@ -0,0 +1,61 @@
1
+ export declare const SCHEMA_VERSION: 1;
2
+ export declare const FEEDBACK_ROUTE: "/feedback";
3
+ export declare const FEEDBACK_KEY_HEADER: "X-Orbit-Feedback-Key";
4
+ export declare const FEEDBACK_ENDPOINT_URL: string;
5
+ export declare const CLI_FEEDBACK_SOURCE: "orbit-cli";
6
+
7
+ export interface FeedbackSysinfo {
8
+ appVersion?: string;
9
+ platform?: string;
10
+ arch?: string;
11
+ /**
12
+ * True when the client runs under WSL. WSL reports `platform: "linux"`, so
13
+ * without this a WSL report is indistinguishable from a native Linux one --
14
+ * and GUI/windowing, xdg-open and sandbox behaviour all differ there.
15
+ * Optional + additive, so schemaVersion stays 1; absent means the report came
16
+ * from a client that predates the field.
17
+ */
18
+ wsl?: boolean;
19
+ electron?: string;
20
+ chrome?: string;
21
+ node?: string;
22
+ llmProvider?: string;
23
+ llmModel?: string;
24
+ galaxyConfigured?: boolean;
25
+ }
26
+
27
+ export interface FeedbackPayload {
28
+ schemaVersion: 1;
29
+ source: "orbit" | "loom-cli" | "orbit-cli";
30
+ /**
31
+ * Opaque beta-tester code (e.g. "orbit-007"), copied from OrbitConfig.testerId.
32
+ * MUST stay top-level: the orbit-feedback worker maps `payload.testerId` to a
33
+ * first-class `tester_id` column. Optional + additive, so schemaVersion stays 1.
34
+ */
35
+ testerId?: string;
36
+ title: string;
37
+ body: string;
38
+ sysinfo?: FeedbackSysinfo;
39
+ activityTail?: string;
40
+ shellTail?: string;
41
+ clientTs: string;
42
+ }
43
+
44
+ export declare function validateFeedbackPayload(obj: unknown): obj is FeedbackPayload;
45
+
46
+ export interface FeedbackActivityEvent {
47
+ timestamp: string;
48
+ kind: string;
49
+ source: string;
50
+ payload?: Record<string, unknown>;
51
+ }
52
+
53
+ export declare function formatActivityTail(
54
+ events: FeedbackActivityEvent[],
55
+ opts?: { maxBytes?: number },
56
+ ): string;
57
+
58
+ export declare function capFeedbackPayload(
59
+ payload: FeedbackPayload,
60
+ opts?: { maxTotalBytes?: number },
61
+ ): FeedbackPayload;
@@ -0,0 +1,146 @@
1
+ // Shared feedback wire contract. Dual-file (.js runtime + .d.ts types) to match
2
+ // team-dispatch-contract: the brain resolves a real .js at runtime, so a single
3
+ // .ts would risk a missing runtime file. No Node imports here -- this file is
4
+ // renderer-safe and shared by the brain, Orbit main, and (by copy) the worker.
5
+
6
+ export const SCHEMA_VERSION = 1;
7
+ export const FEEDBACK_ROUTE = "/feedback";
8
+ export const FEEDBACK_KEY_HEADER = "X-Orbit-Feedback-Key";
9
+
10
+ // Production endpoint base (no trailing slash). Set after `wrangler deploy` of
11
+ // the orbit-feedback worker. Clients may override via the LOOM_FEEDBACK_URL env
12
+ // var for local dev.
13
+ export const FEEDBACK_ENDPOINT_URL = "https://orbit-feedback.dannon-baker.workers.dev";
14
+
15
+ // What the terminal client stamps on its rows. The worker accepts "loom-cli"
16
+ // (0.8 and older) and "orbit-cli" alike.
17
+ export const CLI_FEEDBACK_SOURCE = "orbit-cli";
18
+
19
+ // "orbit-cli" is accepted ahead of time so a mixed-version fleet validates.
20
+ const SOURCES = new Set(["orbit", "loom-cli", "orbit-cli"]);
21
+
22
+ export function validateFeedbackPayload(obj) {
23
+ if (typeof obj !== "object" || obj === null) return false;
24
+ const p = obj;
25
+ if (p.schemaVersion !== SCHEMA_VERSION) return false;
26
+ if (typeof p.source !== "string" || !SOURCES.has(p.source)) return false;
27
+ if (typeof p.title !== "string" || p.title.trim().length === 0) return false;
28
+ if (typeof p.body !== "string") return false;
29
+ if (typeof p.clientTs !== "string") return false;
30
+ // testerId is optional; when present it must be a string (opaque tester code).
31
+ if (p.testerId !== undefined && typeof p.testerId !== "string") return false;
32
+ return true;
33
+ }
34
+
35
+ const ACTIVITY_TAIL_MAX_BYTES = 64 * 1024;
36
+ // Per-field character limits (not bytes) for readability; the wire byte budget
37
+ // is enforced separately by trimLinesToBytes.
38
+ const ARGS_MAX = 200;
39
+ const RESULT_MAX = 500;
40
+ const PROMPT_MAX = 200;
41
+
42
+ const textEncoder = new TextEncoder();
43
+
44
+ function byteLen(s) {
45
+ return textEncoder.encode(s).length;
46
+ }
47
+
48
+ function truncate(s, n) {
49
+ return s.length <= n ? s : s.slice(0, n) + "…";
50
+ }
51
+
52
+ function compactArgs(args) {
53
+ if (args == null) return "";
54
+ let s;
55
+ try {
56
+ s = JSON.stringify(args);
57
+ } catch {
58
+ s = String(args);
59
+ }
60
+ return s ? "args=" + truncate(s, ARGS_MAX) : "";
61
+ }
62
+
63
+ // One line per event. Renders the already-redacted/truncated payload fields the
64
+ // upstream activity hooks produced -- this function adds no redaction of its own.
65
+ // Embedded line breaks (LF, CRLF, or bare CR from progress-style output) are
66
+ // flattened to spaces so each event stays exactly one line, which keeps the
67
+ // byte-budget trimmer from orphaning a fragment of a multi-line event.
68
+ function formatActivityEvent(e) {
69
+ const ts = e && e.timestamp ? e.timestamp : "?";
70
+ const p = (e && e.payload) || {};
71
+ let rawLine;
72
+ switch (e && e.kind) {
73
+ case "user.prompt":
74
+ rawLine = `${ts} user.prompt ${truncate(String(p.text ?? ""), PROMPT_MAX)}`;
75
+ break;
76
+ case "tool.start":
77
+ rawLine = `${ts} tool.start ${p.toolName ?? "?"} ${compactArgs(p.args)}`;
78
+ break;
79
+ case "tool.end": {
80
+ const flag = p.isError ? "✗" : "✓";
81
+ rawLine = `${ts} tool.end ${p.toolName ?? "?"} ${flag} ${truncate(String(p.resultSummary ?? ""), RESULT_MAX)}`;
82
+ break;
83
+ }
84
+ default:
85
+ rawLine = `${ts} ${(e && e.kind) ?? "?"}${e && e.source ? " (" + e.source + ")" : ""}`;
86
+ break;
87
+ }
88
+ return rawLine.trimEnd().replace(/[\r\n]+/g, " ");
89
+ }
90
+
91
+ // Drop oldest (leading) lines until the text fits maxBytes; hard-slice a lone
92
+ // over-budget line as a floor.
93
+ function trimLinesToBytes(text, maxBytes) {
94
+ if (byteLen(text) <= maxBytes) return text;
95
+ const lines = text.split("\n");
96
+ while (lines.length > 1 && byteLen(lines.join("\n")) > maxBytes) {
97
+ lines.shift();
98
+ }
99
+ let out = lines.join("\n");
100
+ if (byteLen(out) > maxBytes) {
101
+ // Binary search for the maximum prefix of 'out' that fits in maxBytes
102
+ // using character offsets to avoid splitting Unicode surrogate pairs.
103
+ const arr = Array.from(out);
104
+ let low = 0;
105
+ let high = arr.length;
106
+ let best = "";
107
+ while (low <= high) {
108
+ const mid = Math.floor((low + high) / 2);
109
+ const candidate = arr.slice(0, mid).join("");
110
+ if (byteLen(candidate) <= maxBytes) {
111
+ best = candidate;
112
+ low = mid + 1;
113
+ } else {
114
+ high = mid - 1;
115
+ }
116
+ }
117
+ out = best;
118
+ }
119
+ return out;
120
+ }
121
+
122
+ export function formatActivityTail(events, opts = {}) {
123
+ const maxBytes = opts.maxBytes ?? ACTIVITY_TAIL_MAX_BYTES;
124
+ const text = (events || []).map(formatActivityEvent).join("\n");
125
+ return trimLinesToBytes(text, maxBytes);
126
+ }
127
+
128
+ const FEEDBACK_MAX_TOTAL_BYTES = 200 * 1024;
129
+
130
+ // Final guard so an assembled payload never bounces off the worker's 256 KB cap.
131
+ // Trim activityTail before shellTail -- the shell tail is closest to what the user
132
+ // actually saw, so it's the last diagnostic we shed. Title/body always stay intact.
133
+ export function capFeedbackPayload(payload, opts = {}) {
134
+ const maxTotalBytes = opts.maxTotalBytes ?? FEEDBACK_MAX_TOTAL_BYTES;
135
+ const totalBytes = (p) => byteLen(JSON.stringify(p));
136
+ let p = { ...payload };
137
+ for (const field of ["activityTail", "shellTail"]) {
138
+ const total = totalBytes(p);
139
+ if (total <= maxTotalBytes) break;
140
+ if (typeof p[field] !== "string" || p[field].length === 0) continue;
141
+ const over = total - maxTotalBytes;
142
+ const target = Math.max(0, byteLen(p[field]) - over);
143
+ p = { ...p, [field]: trimLinesToBytes(p[field], target) };
144
+ }
145
+ return p;
146
+ }
@@ -0,0 +1,30 @@
1
+ export type GalaxyArtifactKind =
2
+ | "server"
3
+ | "history"
4
+ | "dataset"
5
+ | "collection"
6
+ | "job"
7
+ | "invocation"
8
+ | "workflow"
9
+ | "page"
10
+ | "revision"
11
+ | "tool";
12
+ export interface GalaxyArtifactReference {
13
+ start: number;
14
+ end: number;
15
+ href: string;
16
+ kind: GalaxyArtifactKind;
17
+ }
18
+ export function normalizeGalaxyLinkServer(serverUrl: unknown): string | null;
19
+ export function galaxyArtifactUrl(
20
+ serverUrl: unknown,
21
+ kind: GalaxyArtifactKind,
22
+ id?: string | null,
23
+ options?: { pageId?: string },
24
+ ): string | null;
25
+ export function galaxyLinkServerInText(text: string): string | null;
26
+ export function galaxyArtifactReferences(
27
+ text: string,
28
+ fallbackServer?: string | null,
29
+ options?: { trustTextServer?: boolean },
30
+ ): GalaxyArtifactReference[];
@@ -0,0 +1,166 @@
1
+ /** Galaxy navigation shared by Orbit and its shells. No credentials or network I/O. */
2
+ export function normalizeGalaxyLinkServer(serverUrl) {
3
+ if (typeof serverUrl !== "string" || !/^https?:\/\//i.test(serverUrl.trim())) return null;
4
+ try {
5
+ const url = new URL(serverUrl.trim());
6
+ if (url.username || url.password || url.search || url.hash) return null;
7
+ return url.href.replace(/\/+$/, "");
8
+ } catch {
9
+ return null;
10
+ }
11
+ }
12
+
13
+ /**
14
+ * Read/view routes from Galaxy's client router. Pages and stored workflows use
15
+ * /published/... even for private resources: Galaxy still enforces access.
16
+ * Revisions have no UI deep link; use the exact read-only revision API route.
17
+ */
18
+ export function galaxyArtifactUrl(serverUrl, kind, id, options = {}) {
19
+ const base = normalizeGalaxyLinkServer(serverUrl);
20
+ if (!base) return null;
21
+ if (kind === "server") return base;
22
+ if (typeof id !== "string" || !id) return null;
23
+ if (kind === "tool") return `${base}/?tool_id=${encodeURIComponent(id)}`;
24
+ if (!/^[a-f0-9]{16,}$/i.test(id)) return null;
25
+ const encoded = encodeURIComponent(id);
26
+ switch (kind) {
27
+ case "history":
28
+ return `${base}/histories/view?id=${encoded}`;
29
+ case "dataset":
30
+ return `${base}/datasets/${encoded}`;
31
+ case "collection":
32
+ return `${base}/collection/${encoded}/sheet`;
33
+ case "job":
34
+ return `${base}/jobs/${encoded}/view`;
35
+ case "invocation":
36
+ return `${base}/workflows/invocations/${encoded}`;
37
+ case "workflow":
38
+ return `${base}/published/workflow?id=${encoded}`;
39
+ case "page":
40
+ return `${base}/published/page?id=${encoded}`;
41
+ case "revision":
42
+ return /^[a-f0-9]{16,}$/i.test(options.pageId ?? "")
43
+ ? `${base}/api/pages/${encodeURIComponent(options.pageId)}/revisions/${encoded}`
44
+ : null;
45
+ default:
46
+ return null;
47
+ }
48
+ }
49
+
50
+ const FIELD_KINDS = {
51
+ page_id: "page",
52
+ history_id: "history",
53
+ dataset_id: "dataset",
54
+ hda_id: "dataset",
55
+ collection_id: "collection",
56
+ hdca_id: "collection",
57
+ job_id: "job",
58
+ invocation_id: "invocation",
59
+ workflow_id: "workflow",
60
+ stored_workflow_id: "workflow",
61
+ tool_id: "tool",
62
+ last_synced_revision: "revision",
63
+ latest_revision_id: "revision",
64
+ revision_id: "revision",
65
+ galaxy_server_url: "server",
66
+ page_slug: "page",
67
+ };
68
+ const FIELDS = new RegExp(
69
+ `\\b(${Object.keys(FIELD_KINDS).join("|")})\\b["']?\\s*[:=][ \\t]*["'\x60]?([^\\s"'\x60,}<>]+)`,
70
+ "gi",
71
+ );
72
+
73
+ /** Flat metadata only; deliberately not an arbitrary YAML/JSON interpreter. */
74
+ function fieldsIn(text) {
75
+ return Array.from(text.matchAll(FIELDS), (match) => {
76
+ const field = match[1].toLowerCase();
77
+ const kind = FIELD_KINDS[field];
78
+ const value =
79
+ ["server", "tool"].includes(kind) || field === "page_slug"
80
+ ? match[2]
81
+ : match[2].replace(/[.;]+$/, "");
82
+ const start = match.index + match[0].lastIndexOf(match[2]);
83
+ return { field, value, start, end: start + value.length };
84
+ });
85
+ }
86
+
87
+ /** Null means absent; "" means explicit but ambiguous/invalid (do not guess). */
88
+ export function galaxyLinkServerInText(text) {
89
+ const values = fieldsIn(text)
90
+ .filter((f) => f.field === "galaxy_server_url")
91
+ .map((f) => f.value);
92
+ if (!values.length) return null;
93
+ const servers = new Set(values.map(normalizeGalaxyLinkServer));
94
+ return servers.size === 1 ? (servers.values().next().value ?? "") : "";
95
+ }
96
+
97
+ /**
98
+ * Typed references only. Bare hashes, history item numbers, dates, etc. stay text.
99
+ *
100
+ * A `galaxy_server_url` in the text only redirects the other IDs when the
101
+ * caller says the text is trusted metadata (a notebook block Orbit wrote).
102
+ * Otherwise a dataset peek, fetched page, or README could point "Open Galaxy
103
+ * history" links at any host it likes.
104
+ */
105
+ export function galaxyArtifactReferences(text, fallbackServer, options = {}) {
106
+ const fields = fieldsIn(text);
107
+ const server = options.trustTextServer
108
+ ? (galaxyLinkServerInText(text) ?? fallbackServer)
109
+ : fallbackServer;
110
+ const pageIds = new Set(fields.filter((f) => f.field === "page_id").map((f) => f.value));
111
+ const pageId = pageIds.size === 1 ? pageIds.values().next().value : undefined;
112
+ const refs = [];
113
+ for (const field of fields) {
114
+ // An invocation response's workflow_id names an internal workflow version,
115
+ // not the stored workflow required by Galaxy's published-workflow view.
116
+ if (field.field === "workflow_id" && fields.some((f) => f.field === "invocation_id")) continue;
117
+ const kind = FIELD_KINDS[field.field];
118
+ const href = galaxyArtifactUrl(
119
+ // Untrusted, the server field still links to the URL it shows -- just not the other IDs.
120
+ kind === "server" && !options.trustTextServer ? field.value : server,
121
+ kind,
122
+ field.field === "page_slug" ? pageId : field.value,
123
+ { pageId },
124
+ );
125
+ if (href && !["null", "~", "<none>"].includes(field.value)) {
126
+ refs.push({ start: field.start, end: field.end, href, kind });
127
+ }
128
+ }
129
+ // Prose such as `Dataset ID: abc...` or `invocation abc...`. Require an
130
+ // encoded ID's length here so ordinary words/numeric history HIDs don't link.
131
+ const prose =
132
+ /\b(page|history|dataset|collection|job|invocation|workflow)(?:[ _]id)?\s*[:=#]?\s*([a-f0-9]{16,})\b/gi;
133
+ for (const match of text.matchAll(prose)) {
134
+ const start = match.index + match[0].lastIndexOf(match[2]);
135
+ if (fields.some((r) => start >= r.start && start < r.end)) continue;
136
+ const kind = match[1].toLowerCase();
137
+ const href = galaxyArtifactUrl(server, kind, match[2]);
138
+ if (href) refs.push({ start, end: start + match[2].length, href, kind });
139
+ }
140
+ // Markdown deliberately doesn't auto-link inline-code URLs. Recognize Galaxy
141
+ // view/API routes so an explicit URL still works there, without needing a
142
+ // connected profile or rewriting links to another server.
143
+ for (const match of text.matchAll(/https?:\/\/[^\s<>"'`]+/gi)) {
144
+ const href = match[0].replace(/[.,;!?)\]}]+$/, "");
145
+ if (refs.some((r) => match.index < r.end && match.index + href.length > r.start)) continue;
146
+ let url;
147
+ try {
148
+ url = new URL(href);
149
+ } catch {
150
+ continue;
151
+ }
152
+ if (url.username || url.password) continue;
153
+ const path = url.pathname;
154
+ let kind;
155
+ if (/\/api\/pages\/[^/]+\/revisions\/[^/]+$/.test(path)) kind = "revision";
156
+ else if (/\/published\/(page|workflow)$/.test(path))
157
+ kind = path.endsWith("page") ? "page" : "workflow";
158
+ else if (/\/histories\/view$/.test(path)) kind = "history";
159
+ else if (/\/workflows\/invocations\/[^/]+(?:\/[^/]+)?$/.test(path)) kind = "invocation";
160
+ else if (/\/jobs\/[^/]+\/view$/.test(path)) kind = "job";
161
+ else if (/\/collection\/[^/]+\/sheet$/.test(path)) kind = "collection";
162
+ else if (/\/datasets\/[^/]+(?:\/[^/]+)?$/.test(path)) kind = "dataset";
163
+ if (kind) refs.push({ start: match.index, end: match.index + href.length, href, kind });
164
+ }
165
+ return refs.sort((a, b) => a.start - b.start);
166
+ }