@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.
- package/CHANGELOG.md +123 -0
- package/LICENSE +21 -0
- package/README.md +760 -5
- package/bin/legacy-mcp-config.js +363 -0
- package/bin/orbit.js +810 -0
- package/bin/pi-extension-path.d.ts +4 -0
- package/bin/pi-extension-path.js +44 -0
- package/bin/provider-auth.d.ts +22 -0
- package/bin/provider-auth.js +77 -0
- package/bin/thinking-pref.js +38 -0
- package/bin/update-check.d.ts +15 -0
- package/bin/update-check.js +137 -0
- package/extensions/cli-update/index.ts +32 -0
- package/extensions/orbit/activity-hooks.ts +141 -0
- package/extensions/orbit/activity.ts +116 -0
- package/extensions/orbit/agent-dir.ts +11 -0
- package/extensions/orbit/auto-resume.ts +352 -0
- package/extensions/orbit/config.ts +3 -0
- package/extensions/orbit/confusables.ts +1 -0
- package/extensions/orbit/context.ts +957 -0
- package/extensions/orbit/dashboard-commands.ts +191 -0
- package/extensions/orbit/dashboard-store.ts +473 -0
- package/extensions/orbit/dashboard-tools.ts +1185 -0
- package/extensions/orbit/empty-completion.ts +99 -0
- package/extensions/orbit/evidence-gate.ts +1276 -0
- package/extensions/orbit/evidence-override-command.ts +294 -0
- package/extensions/orbit/exec-guard/bash-risk.ts +1408 -0
- package/extensions/orbit/exec-guard/gate.ts +181 -0
- package/extensions/orbit/exec-guard/guardian-config.ts +54 -0
- package/extensions/orbit/exec-guard/index.ts +1 -0
- package/extensions/orbit/exec-guard/model-tier.ts +36 -0
- package/extensions/orbit/exec-guard/path-jail.ts +54 -0
- package/extensions/orbit/exec-guard/policy.ts +342 -0
- package/extensions/orbit/exec-guard/runtime-state.ts +15 -0
- package/extensions/orbit/exec-guard/sensitive-read.ts +525 -0
- package/extensions/orbit/exec-guard/types.ts +42 -0
- package/extensions/orbit/execution-commands.ts +134 -0
- package/extensions/orbit/feedback-command.ts +81 -0
- package/extensions/orbit/feedback.ts +96 -0
- package/extensions/orbit/galaxy-api.test.ts +122 -0
- package/extensions/orbit/galaxy-api.ts +606 -0
- package/extensions/orbit/galaxy-artifact-link-guidance.ts +5 -0
- package/extensions/orbit/galaxy-destructive-gate.ts +113 -0
- package/extensions/orbit/galaxy-enrich.ts +1108 -0
- package/extensions/orbit/galaxy-failure.ts +101 -0
- package/extensions/orbit/galaxy-fixture.ts +126 -0
- package/extensions/orbit/galaxy-history-guard.ts +233 -0
- package/extensions/orbit/galaxy-job-block.ts +347 -0
- package/extensions/orbit/galaxy-job-ops.ts +288 -0
- package/extensions/orbit/galaxy-live-source.ts +929 -0
- package/extensions/orbit/galaxy-markdown-adapter.ts +248 -0
- package/extensions/orbit/galaxy-op-policy.ts +332 -0
- package/extensions/orbit/galaxy-ops-tools.ts +326 -0
- package/extensions/orbit/galaxy-output-store.ts +80 -0
- package/extensions/orbit/galaxy-output.ts +346 -0
- package/extensions/orbit/galaxy-page-binding.ts +222 -0
- package/extensions/orbit/galaxy-page-markdown-guidance.ts +15 -0
- package/extensions/orbit/galaxy-page-ops.ts +159 -0
- package/extensions/orbit/galaxy-page-sync.test.ts +417 -0
- package/extensions/orbit/galaxy-page-sync.ts +318 -0
- package/extensions/orbit/galaxy-pages-api.ts +203 -0
- package/extensions/orbit/galaxy-pages-sync.ts +409 -0
- package/extensions/orbit/galaxy-poll-guard.ts +189 -0
- package/extensions/orbit/galaxy-poller.ts +706 -0
- package/extensions/orbit/galaxy-provenance.ts +623 -0
- package/extensions/orbit/galaxy-reconcile.ts +956 -0
- package/extensions/orbit/galaxy-recovery.ts +176 -0
- package/extensions/orbit/galaxy-repeat-guard.ts +55 -0
- package/extensions/orbit/galaxy-submission-capture.ts +707 -0
- package/extensions/orbit/galaxy-submission.ts +502 -0
- package/extensions/orbit/galaxy-tool-source.ts +78 -0
- package/extensions/orbit/galaxy-tool-wrap.ts +234 -0
- package/extensions/orbit/galaxy-udt-block.ts +127 -0
- package/extensions/orbit/galaxy-upload-tus.ts +334 -0
- package/extensions/orbit/galaxy-upload.ts +300 -0
- package/extensions/orbit/galaxy-version-capture.ts +65 -0
- package/extensions/orbit/git.ts +173 -0
- package/extensions/orbit/harness-block-fields.ts +597 -0
- package/extensions/orbit/index.ts +593 -0
- package/extensions/orbit/init-gate.ts +284 -0
- package/extensions/orbit/instructions-command.ts +136 -0
- package/extensions/orbit/invocation-failure-hint.ts +57 -0
- package/extensions/orbit/iwc-plan-check.ts +94 -0
- package/extensions/orbit/lesson-command.ts +303 -0
- package/extensions/orbit/lesson-hint.ts +245 -0
- package/extensions/orbit/lesson-nudge.ts +106 -0
- package/extensions/orbit/lessons/compose.ts +177 -0
- package/extensions/orbit/lessons/counters.ts +120 -0
- package/extensions/orbit/lessons/enabled.ts +116 -0
- package/extensions/orbit/lessons/matcher.ts +346 -0
- package/extensions/orbit/lessons/paths.ts +286 -0
- package/extensions/orbit/lessons/pi-event-contract.ts +105 -0
- package/extensions/orbit/lessons/propose-replay.ts +103 -0
- package/extensions/orbit/lessons/propose-tool.ts +222 -0
- package/extensions/orbit/lessons/propose.ts +502 -0
- package/extensions/orbit/lessons/replay.ts +89 -0
- package/extensions/orbit/lessons/reproduction-index.ts +83 -0
- package/extensions/orbit/lessons/search-tool.ts +99 -0
- package/extensions/orbit/lessons/search.ts +136 -0
- package/extensions/orbit/lessons/store.ts +415 -0
- package/extensions/orbit/lessons/text.ts +36 -0
- package/extensions/orbit/lessons/types.ts +69 -0
- package/extensions/orbit/lessons/user-dir.ts +24 -0
- package/extensions/orbit/lessons/user-lesson.ts +88 -0
- package/extensions/orbit/lessons/wrapper.ts +61 -0
- package/extensions/orbit/lessons-command.ts +147 -0
- package/extensions/orbit/local-exec.ts +39 -0
- package/extensions/orbit/mcp-output.ts +179 -0
- package/extensions/orbit/mcp-servers.test.ts +24 -0
- package/extensions/orbit/mcp-servers.ts +25 -0
- package/extensions/orbit/model-script.ts +351 -0
- package/extensions/orbit/notebook-anchors.ts +342 -0
- package/extensions/orbit/notebook-writer.ts +863 -0
- package/extensions/orbit/observation-allowlists.ts +1109 -0
- package/extensions/orbit/observation-replay.ts +148 -0
- package/extensions/orbit/observation-triggers.ts +563 -0
- package/extensions/orbit/observation-ui.ts +175 -0
- package/extensions/orbit/observations-command.ts +421 -0
- package/extensions/orbit/observations-config.ts +200 -0
- package/extensions/orbit/observations.ts +1381 -0
- package/extensions/orbit/profiles.ts +270 -0
- package/extensions/orbit/progress-updates.ts +95 -0
- package/extensions/orbit/proposal-commands.ts +882 -0
- package/extensions/orbit/provenance-fs-file.ts +59 -0
- package/extensions/orbit/provenance-fs.ts +32 -0
- package/extensions/orbit/provenance-memory-fs.ts +59 -0
- package/extensions/orbit/record-store-file.ts +177 -0
- package/extensions/orbit/record-store-page.ts +77 -0
- package/extensions/orbit/record-store-seam.ts +47 -0
- package/extensions/orbit/record-store.ts +149 -0
- package/extensions/orbit/registry-carrier.ts +107 -0
- package/extensions/orbit/registry-eligibility.ts +95 -0
- package/extensions/orbit/registry-evaluation.ts +403 -0
- package/extensions/orbit/registry-evaluator.ts +519 -0
- package/extensions/orbit/registry-import.ts +91 -0
- package/extensions/orbit/registry-lock.ts +221 -0
- package/extensions/orbit/registry-memory-fs.ts +42 -0
- package/extensions/orbit/registry-page-carrier.ts +103 -0
- package/extensions/orbit/registry-proposal.ts +1348 -0
- package/extensions/orbit/registry-runtime.ts +234 -0
- package/extensions/orbit/registry-schema.ts +735 -0
- package/extensions/orbit/registry-submitter.ts +59 -0
- package/extensions/orbit/registry-templates.ts +346 -0
- package/extensions/orbit/registry.ts +476 -0
- package/extensions/orbit/sandbox/index.ts +120 -0
- package/extensions/orbit/sandbox/sandbox-bash.ts +93 -0
- package/extensions/orbit/sandbox/sandbox-config.ts +87 -0
- package/extensions/orbit/sandbox/sandbox-init-message.ts +28 -0
- package/extensions/orbit/secret-redaction.ts +247 -0
- package/extensions/orbit/session-index/cwd.ts +18 -0
- package/extensions/orbit/session-index/db.ts +56 -0
- package/extensions/orbit/session-index/indexer.ts +154 -0
- package/extensions/orbit/session-index/is-enabled.ts +17 -0
- package/extensions/orbit/session-index/parse.ts +254 -0
- package/extensions/orbit/session-index/query.ts +209 -0
- package/extensions/orbit/session-index/schema.ts +72 -0
- package/extensions/orbit/session-index/tools.ts +152 -0
- package/extensions/orbit/session-lifecycle.ts +321 -0
- package/extensions/orbit/skill-triggers.ts +159 -0
- package/extensions/orbit/skills-command.ts +48 -0
- package/extensions/orbit/skills-discovery.ts +435 -0
- package/extensions/orbit/skills.ts +84 -0
- package/extensions/orbit/sra-import-gate.ts +77 -0
- package/extensions/orbit/state.ts +265 -0
- package/extensions/orbit/submission-replay.ts +147 -0
- package/extensions/orbit/sync-command.ts +157 -0
- package/extensions/orbit/teams/critic-parser.ts +93 -0
- package/extensions/orbit/teams/dispatcher.ts +210 -0
- package/extensions/orbit/teams/is-enabled.ts +22 -0
- package/extensions/orbit/teams/tool.ts +187 -0
- package/extensions/orbit/teams/types.ts +57 -0
- package/extensions/orbit/teams/validate.ts +42 -0
- package/extensions/orbit/tester-id-command.ts +133 -0
- package/extensions/orbit/tools-sync.ts +279 -0
- package/extensions/orbit/tools.ts +1300 -0
- package/extensions/orbit/types.ts +41 -0
- package/extensions/orbit/ui-bridge.ts +55 -0
- package/extensions/orbit/ulid.ts +60 -0
- package/extensions/orbit/user-instructions.ts +388 -0
- package/extensions/orbit/vendor/skills/_catalog.json +68 -0
- package/extensions/orbit/vendor/skills/_manifest.json +1106 -0
- package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/SKILL.md +112 -0
- package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/_provenance.json +218 -0
- package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/_required_tools.json +33 -0
- package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/_verify.json +4 -0
- package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/cli/add.json +50 -0
- package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/cli/draft-extract.json +51 -0
- package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/cli/draft-next-step.json +42 -0
- package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/cli/draft-validate.json +140 -0
- package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/cli/list.json +32 -0
- package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/notes/galaxy-tool-job-failure-reference.md +137 -0
- package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/notes/open-requirements-ledger.md +173 -0
- package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/schemas/galaxy-tool-summary.schema.json +175 -0
- package/extensions/orbit/vendor/skills/advance-galaxy-draft-step/references/schemas/galaxy-workflow-draft.schema.json +2727 -0
- package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/SKILL.md +82 -0
- package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/_provenance.json +133 -0
- package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/_required_tools.json +16 -0
- package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/_verify.json +4 -0
- package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/references/cli/draft-validate.json +140 -0
- package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/references/notes/galaxy-workflow-draft-format.md +135 -0
- package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/references/notes/open-requirements-ledger.md +173 -0
- package/extensions/orbit/vendor/skills/apply-galaxy-workflow-changeset/references/schemas/galaxy-workflow-draft.schema.json +2727 -0
- package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/SKILL.md +84 -0
- package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/_provenance.json +166 -0
- package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/_verify.json +29 -0
- package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/notes/galaxy-workflow-testability-design.md +136 -0
- package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/notes/iwc-shortcuts-anti-patterns.md +357 -0
- package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/notes/iwc-test-data-conventions.md +308 -0
- package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/notes/planemo-asserts-idioms.md +246 -0
- package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/notes/planemo-workflow-test-architecture.md +151 -0
- package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/schemas/galaxy-workflow-test-plan.schema.json +552 -0
- package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/schemas/summary-galaxy-workflow.schema.json +687 -0
- package/extensions/orbit/vendor/skills/changeset-to-galaxy-test-plan/references/schemas/tests-format.schema.json +7328 -0
- package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/SKILL.md +127 -0
- package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/_provenance.json +198 -0
- package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/_required_tools.json +16 -0
- package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/_verify.json +4 -0
- package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/references/cli/convert.json +123 -0
- package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/references/notes/galaxy-data-flow-draft-contract.md +129 -0
- package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/references/notes/iwc-shortcuts-anti-patterns.md +357 -0
- package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/references/notes/iwc-test-data-conventions.md +308 -0
- package/extensions/orbit/vendor/skills/compare-against-iwc-exemplar/references/notes/open-requirements-ledger.md +173 -0
- package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/SKILL.md +73 -0
- package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/_provenance.json +160 -0
- package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/_verify.json +4 -0
- package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/galaxy-collection-semantics.md +43 -0
- package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/galaxy-collection-semantics.yml +1250 -0
- package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/galaxy-tool-job-failure-reference.md +137 -0
- package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/galaxy-workflow-invocation-failure-reference.md +138 -0
- package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/iwc-shortcuts-anti-patterns.md +357 -0
- package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/nextflow-operators-to-galaxy-collection-recipes.md +98 -0
- package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/planemo-asserts-idioms.md +246 -0
- package/extensions/orbit/vendor/skills/debug-galaxy-workflow-output/references/notes/planemo-workflow-test-architecture.md +151 -0
- package/extensions/orbit/vendor/skills/discover-shed-tool/SKILL.md +174 -0
- package/extensions/orbit/vendor/skills/discover-shed-tool/_provenance.json +130 -0
- package/extensions/orbit/vendor/skills/discover-shed-tool/_required_tools.json +18 -0
- package/extensions/orbit/vendor/skills/discover-shed-tool/_verify.json +17 -0
- package/extensions/orbit/vendor/skills/discover-shed-tool/references/cli/tool-revisions.json +48 -0
- package/extensions/orbit/vendor/skills/discover-shed-tool/references/cli/tool-search.json +95 -0
- package/extensions/orbit/vendor/skills/discover-shed-tool/references/cli/tool-versions.json +39 -0
- package/extensions/orbit/vendor/skills/discover-shed-tool/references/notes/component-tool-shed-search.md +230 -0
- package/extensions/orbit/vendor/skills/discover-shed-tool/references/schemas/galaxy-tool-discovery.schema.json +231 -0
- package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/SKILL.md +93 -0
- package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/_provenance.json +201 -0
- package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/_required_tools.json +28 -0
- package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/_verify.json +17 -0
- package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/cli/planemo.md +44 -0
- package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/cli/validate-tests.json +40 -0
- package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/notes/galaxy-workflow-testability-design.md +136 -0
- package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/notes/iwc-shortcuts-anti-patterns.md +357 -0
- package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/notes/iwc-test-data-conventions.md +308 -0
- package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/notes/planemo-asserts-idioms.md +246 -0
- package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/notes/planemo-workflow-test-architecture.md +151 -0
- package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/schemas/galaxy-workflow-test-plan.schema.json +552 -0
- package/extensions/orbit/vendor/skills/implement-galaxy-workflow-test/references/schemas/tests-format.schema.json +7328 -0
- package/extensions/orbit/vendor/skills/interview-to-galaxy-workflow-changeset/SKILL.md +81 -0
- package/extensions/orbit/vendor/skills/interview-to-galaxy-workflow-changeset/_provenance.json +93 -0
- package/extensions/orbit/vendor/skills/interview-to-galaxy-workflow-changeset/_verify.json +17 -0
- package/extensions/orbit/vendor/skills/interview-to-galaxy-workflow-changeset/references/notes/open-requirements-ledger.md +173 -0
- package/extensions/orbit/vendor/skills/interview-to-galaxy-workflow-changeset/references/schemas/summary-galaxy-workflow.schema.json +687 -0
- package/extensions/orbit/vendor/skills/pipeline-update-interview-to-galaxy/SKILL.md +81 -0
- package/extensions/orbit/vendor/skills/pipeline-update-interview-to-galaxy/_assembly.json +23 -0
- package/extensions/orbit/vendor/skills/run-workflow-test/SKILL.md +79 -0
- package/extensions/orbit/vendor/skills/run-workflow-test/_provenance.json +125 -0
- package/extensions/orbit/vendor/skills/run-workflow-test/_required_tools.json +28 -0
- package/extensions/orbit/vendor/skills/run-workflow-test/_verify.json +4 -0
- package/extensions/orbit/vendor/skills/run-workflow-test/references/cli/planemo.md +44 -0
- package/extensions/orbit/vendor/skills/run-workflow-test/references/cli/validate-tests.json +40 -0
- package/extensions/orbit/vendor/skills/run-workflow-test/references/notes/galaxy-workflow-invocation-failure-reference.md +138 -0
- package/extensions/orbit/vendor/skills/run-workflow-test/references/notes/planemo-asserts-idioms.md +246 -0
- package/extensions/orbit/vendor/skills/run-workflow-test/references/notes/planemo-workflow-test-architecture.md +151 -0
- package/extensions/orbit/vendor/skills/run-workflow-test/references/schemas/tests-format.schema.json +7328 -0
- package/extensions/orbit/vendor/skills/skills/LICENSE +21 -0
- package/extensions/orbit/vendor/skills/skills/collection-manipulation/README.md +39 -0
- package/extensions/orbit/vendor/skills/skills/collection-manipulation/SKILL.md +892 -0
- package/extensions/orbit/vendor/skills/skills/collection-manipulation/references/api-patterns.md +927 -0
- package/extensions/orbit/vendor/skills/skills/collection-manipulation/references/apply-rules.md +1482 -0
- package/extensions/orbit/vendor/skills/skills/collection-manipulation/references/test-patterns.md +996 -0
- package/extensions/orbit/vendor/skills/skills/collection-manipulation/references/tools.md +609 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-data-libraries/SKILL.md +170 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-integration/README.md +162 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-integration/SKILL.md +99 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-integration/examples/README.md +119 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-integration/examples/tool-checking.md +419 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-integration/examples/workflow-testing.md +617 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-integration/galaxy-integration.md +614 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-integration/jupyterlite/SKILL.md +144 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-integration/jupyterlite/examples/average_col3.ipynb +68 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-integration/jupyterlite/examples/extract_sample_metadata.ipynb +190 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-integration/jupyterlite/examples/variant_annotation.ipynb +816 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-integration/jupyterlite/examples/vcp_variant_map.ipynb +443 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-integration/scripts/README.md +165 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-integration/scripts/galaxy_tool_checker.py +521 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-mcp-reference/SKILL.md +128 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-mcp-reference/gotchas.md +135 -0
- package/extensions/orbit/vendor/skills/skills/galaxy-mcp-reference/history-access.md +132 -0
- package/extensions/orbit/vendor/skills/skills/reproduciblify/SKILL.md +146 -0
- package/extensions/orbit/vendor/skills/skills/reproduciblify/references/directives.yml +496 -0
- package/extensions/orbit/vendor/skills/skills/udt-authoring/SKILL.md +245 -0
- package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/01-minimal-echo.yml +27 -0
- package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/02-single-data-in-out.yml +27 -0
- package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/03-multiple-data-js.yml +28 -0
- package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/04-select-integer-boolean.yml +41 -0
- package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/05-resource-requirements.yml +25 -0
- package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/06-biocontainer-tool.yml +38 -0
- package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/07-inline-python-script.yml +56 -0
- package/extensions/orbit/vendor/skills/skills/udt-authoring/examples/08-configfile-script.yml +74 -0
- package/extensions/orbit/vendor/skills/skills/udt-authoring/references/common-mistakes.md +76 -0
- package/extensions/orbit/vendor/skills/skills/udt-authoring/references/schema-reference.md +155 -0
- package/extensions/orbit/vendor/skills/skills/udt-authoring/references/templating.md +196 -0
- package/extensions/orbit/vendor/skills/skills/udt-authoring/scripts/validate.py +109 -0
- package/extensions/orbit/vendor/skills/skills/workflow-reports/README.md +71 -0
- package/extensions/orbit/vendor/skills/skills/workflow-reports/SKILL.md +190 -0
- package/extensions/orbit/vendor/skills/skills/workflow-reports/examples/histology-staining.md +145 -0
- package/extensions/orbit/vendor/skills/skills/workflow-reports/examples/tissue-microarray-analysis.md +176 -0
- package/extensions/orbit/vendor/skills/skills/workflow-reports/references/directives.md +415 -0
- package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/SKILL.md +98 -0
- package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/_provenance.json +93 -0
- package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/_required_tools.json +28 -0
- package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/_verify.json +17 -0
- package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/references/cli/convert.json +123 -0
- package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/references/cli/foundry.md +25 -0
- package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/references/cli/validate.json +142 -0
- package/extensions/orbit/vendor/skills/summarize-galaxy-workflow/references/schemas/summary-galaxy-workflow.schema.json +687 -0
- package/extensions/orbit/vendor/skills/validate-galaxy-workflow/SKILL.md +74 -0
- package/extensions/orbit/vendor/skills/validate-galaxy-workflow/_provenance.json +71 -0
- package/extensions/orbit/vendor/skills/validate-galaxy-workflow/_required_tools.json +16 -0
- package/extensions/orbit/vendor/skills/validate-galaxy-workflow/_verify.json +4 -0
- package/extensions/orbit/vendor/skills/validate-galaxy-workflow/references/cli/validate.json +142 -0
- package/extensions/orbit/vendor/skills/validate-galaxy-workflow/references/notes/galaxy-workflow-invocation-failure-reference.md +138 -0
- package/extensions/orbit/vendor/skills/validate-galaxy-workflow/references/notes/galaxy-workflow-testability-design.md +136 -0
- package/extensions/orbit/vendor-skills.ts +283 -0
- package/extensions/orbit/workspace-state-dir.ts +46 -0
- package/extensions/orbit-handoff/index.ts +73 -0
- package/extensions/orbit-handoff/orbit-launcher.ts +142 -0
- package/extensions/whats-new/index.ts +98 -0
- package/lessons/LICENSE +17 -0
- package/lessons/README.md +117 -0
- package/lessons/SCHEMA.md +187 -0
- package/lessons/build-snapshot.mjs +311 -0
- package/lessons/data/downloaded-file-is-not-what-its-extension-says.md +61 -0
- package/lessons/galaxy-api/403-history-is-a-hard-stop.md +57 -0
- package/lessons/galaxy-api/collection-into-single-dataset-input.md +61 -0
- package/lessons/galaxy-api/connectedvalue-in-command-line.md +62 -0
- package/lessons/galaxy-api/hid-is-not-an-id.md +59 -0
- package/lessons/galaxy-api/invoke-workflow-inputs-not-params.md +60 -0
- package/lessons/galaxy-api/repeat-param-pipe-keys.md +63 -0
- package/lessons/galaxy-api/run-tool-returns-on-submit.md +61 -0
- package/lessons/galaxy-tools/reference-index-not-on-server.md +62 -0
- package/lessons/log.md +42 -0
- package/lessons/reproduction/input-population-mismatch.md +59 -0
- package/lessons/reproduction/methods-text-vs-executed-parameters.md +62 -0
- package/lessons/snapshot.json +1004 -0
- package/lessons/stats/de-contrast-direction-and-sample-labels.md +63 -0
- package/lessons/stats/na-coerced-to-zero-in-filters.md +59 -0
- package/lessons/validate.mjs +1219 -0
- package/package.json +103 -5
- package/shared/agent-prompt.d.ts +8 -0
- package/shared/agent-prompt.js +15 -0
- package/shared/approval-prompt.d.ts +4 -0
- package/shared/approval-prompt.js +61 -0
- package/shared/brain-env.d.ts +14 -0
- package/shared/brain-env.js +108 -0
- package/shared/brain-exit.d.ts +20 -0
- package/shared/brain-exit.js +149 -0
- package/shared/capability-profile.d.ts +16 -0
- package/shared/capability-profile.js +75 -0
- package/shared/config-source.d.ts +13 -0
- package/shared/config-source.js +17 -0
- package/shared/confusables.d.ts +3 -0
- package/shared/confusables.js +93 -0
- package/shared/custom-provider.d.ts +45 -0
- package/shared/custom-provider.js +169 -0
- package/shared/dashboard-contract.d.ts +123 -0
- package/shared/dashboard-contract.js +560 -0
- package/shared/dashboard-layout-store.d.ts +44 -0
- package/shared/dashboard-layout-store.js +281 -0
- package/shared/feedback-contract.d.ts +61 -0
- package/shared/feedback-contract.js +146 -0
- package/shared/galaxy-artifact-links.d.ts +30 -0
- package/shared/galaxy-artifact-links.js +166 -0
- package/shared/galaxy-credentials.d.ts +26 -0
- package/shared/galaxy-credentials.js +71 -0
- package/shared/galaxy-destructive.d.ts +21 -0
- package/shared/galaxy-destructive.js +269 -0
- package/shared/galaxy-guidance.d.ts +2 -0
- package/shared/galaxy-guidance.js +53 -0
- package/shared/galaxy-live-contract.d.ts +92 -0
- package/shared/galaxy-live-contract.js +180 -0
- package/shared/galaxy-page-cells.d.ts +2 -0
- package/shared/galaxy-page-cells.js +22 -0
- package/shared/galaxy-tool-family.d.ts +5 -0
- package/shared/galaxy-tool-family.js +27 -0
- package/shared/galaxy-url.d.ts +1 -0
- package/shared/galaxy-url.js +48 -0
- package/shared/gtn.d.ts +31 -0
- package/shared/gtn.js +253 -0
- package/shared/handled-prompt.d.ts +3 -0
- package/shared/handled-prompt.js +26 -0
- package/shared/known-provider.d.ts +11 -0
- package/shared/known-provider.js +89 -0
- package/shared/lesson-hint-marker.d.ts +7 -0
- package/shared/lesson-hint-marker.js +32 -0
- package/shared/lesson-rules.d.ts +150 -0
- package/shared/lesson-rules.js +203 -0
- package/shared/malformed-object-ids.d.ts +1 -0
- package/shared/malformed-object-ids.js +37 -0
- package/shared/markdown-sections.d.ts +10 -0
- package/shared/markdown-sections.js +118 -0
- package/shared/notebook-fences.d.ts +12 -0
- package/shared/notebook-fences.js +53 -0
- package/shared/observation-contract.d.ts +91 -0
- package/shared/observation-contract.js +544 -0
- package/shared/orbit-config.d.ts +226 -0
- package/shared/orbit-config.js +189 -0
- package/shared/orbit-env.d.ts +13 -0
- package/shared/orbit-env.js +124 -0
- package/shared/orbit-shell-contract.d.ts +74 -0
- package/shared/orbit-shell-contract.js +30 -0
- package/shared/page-content-hash.d.ts +1 -0
- package/shared/page-content-hash.js +13 -0
- package/shared/pi-default-model.d.ts +7 -0
- package/shared/pi-default-model.js +41 -0
- package/shared/prompt-blocks.d.ts +87 -0
- package/shared/prompt-blocks.js +898 -0
- package/shared/provider-auth-caps.d.ts +22 -0
- package/shared/provider-auth-caps.js +57 -0
- package/shared/random.d.ts +2 -0
- package/shared/random.js +15 -0
- package/shared/record-excerpt.d.ts +4 -0
- package/shared/record-excerpt.js +16 -0
- package/shared/redirect-guard.d.ts +36 -0
- package/shared/redirect-guard.js +235 -0
- package/shared/skills-pin.d.ts +8 -0
- package/shared/skills-pin.js +9 -0
- package/shared/skills-router.d.ts +17 -0
- package/shared/skills-router.js +63 -0
- package/shared/sra-import.d.ts +34 -0
- package/shared/sra-import.js +206 -0
- package/shared/state-dir.d.ts +21 -0
- package/shared/state-dir.js +240 -0
- package/shared/team-dispatch-contract.d.ts +28 -0
- package/shared/team-dispatch-contract.js +1 -0
- package/shared/truncate-middle.d.ts +11 -0
- package/shared/truncate-middle.js +68 -0
- package/shared/version-compare.d.ts +10 -0
- package/shared/version-compare.js +74 -0
- package/shared/whats-new.d.ts +31 -0
- package/shared/whats-new.js +83 -0
- package/shared/wsl.d.ts +10 -0
- package/shared/wsl.js +24 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* galaxy-ops' get_page and update_page, widened with what galaxy-ops' next release gives them
|
|
3
|
+
* (#155's S6): a `content_hash` on every page read and written, an `expect_hash` that refuses a
|
|
4
|
+
* write to a page that changed since it was read, section edits by heading line, and a refusal
|
|
5
|
+
* of directive ids that are not encoded ids. Delete this module when galaxy-ops ships S6.
|
|
6
|
+
*
|
|
7
|
+
* Only that operation behaviour lives here. What the brain adds on top -- remembering the page it
|
|
8
|
+
* showed the model, repairing a stale hash when the section is untouched, serializing with the
|
|
9
|
+
* record poller -- is policy (galaxy-op-policy.ts), so it survives this module's deletion.
|
|
10
|
+
*
|
|
11
|
+
* Writes go to Galaxy directly through the operation's context, never through
|
|
12
|
+
* `recordStore.write()`: the model is editing a Galaxy page, which may or may not be the record,
|
|
13
|
+
* and the record store's own version check is the policy's business, not the operation's.
|
|
14
|
+
*/
|
|
15
|
+
import {
|
|
16
|
+
getPageOp,
|
|
17
|
+
GalaxyValidationError,
|
|
18
|
+
httpError,
|
|
19
|
+
updatePageOp,
|
|
20
|
+
type AnyOperation,
|
|
21
|
+
type InputOf,
|
|
22
|
+
type Operation,
|
|
23
|
+
type PageDetail,
|
|
24
|
+
} from "@galaxyproject/galaxy-ops";
|
|
25
|
+
import { applySectionEdit, repeatedHeadingRefusal } from "../../shared/markdown-sections.js";
|
|
26
|
+
import { malformedObjectIds } from "../../shared/malformed-object-ids.js";
|
|
27
|
+
import { pageContentHash } from "../../shared/page-content-hash.js";
|
|
28
|
+
import type { GalaxyBridge } from "./galaxy-ops-tools";
|
|
29
|
+
|
|
30
|
+
interface PageBody {
|
|
31
|
+
content_editor?: string | null;
|
|
32
|
+
content?: string | null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** The editable markdown, as galaxy-ops' contentHash reads it: the hash is taken over this. */
|
|
36
|
+
export function pageBody(page: PageBody): string {
|
|
37
|
+
return page.content_editor || page.content || "";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function withoutRendered<T extends PageBody>(page: T): Omit<T, "content"> {
|
|
41
|
+
const { content: _rendered, ...rest } = page;
|
|
42
|
+
return rest;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** The refusal for directive ids that are not encoded ids; create_page's policy says the same. */
|
|
46
|
+
export function malformedIdsRefusal(found: readonly string[]): string {
|
|
47
|
+
return `These Galaxy directive arguments do not hold an encoded id: ${found.join(", ")}. Galaxy has nothing to look up behind them, so the embed comes out empty. Copy the object's id from a Galaxy tool's answer instead (get_history_contents gives every dataset and collection in a history with its id).`;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
type WithHash<T> = T & { content_hash: string };
|
|
51
|
+
|
|
52
|
+
// Neither page op is budgeted, and a budget typed over the base op's output would not fit the
|
|
53
|
+
// bridge's, so the bridges start from the rest of each op.
|
|
54
|
+
const { budget: _getPageBudget, ...getPageBase } = getPageOp;
|
|
55
|
+
const { budget: _updatePageBudget, ...updatePageBase } = updatePageOp;
|
|
56
|
+
|
|
57
|
+
const getPageBridgeOp: Operation<typeof getPageOp.input, WithHash<Omit<PageDetail, "content">>> = {
|
|
58
|
+
...getPageBase,
|
|
59
|
+
async run(i, ctx) {
|
|
60
|
+
// One GET for both: galaxy-ops strips the rendered body unless asked for it, and the hash
|
|
61
|
+
// has to be taken over the raw page the server sent.
|
|
62
|
+
const raw = await getPageOp.run({ ...i, includeRendered: true }, ctx);
|
|
63
|
+
const content_hash = pageContentHash(pageBody(raw));
|
|
64
|
+
return { ...(i.includeRendered ? raw : withoutRendered(raw)), content_hash };
|
|
65
|
+
},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
export const getPageBridge: GalaxyBridge = {
|
|
69
|
+
op: getPageBridgeOp as unknown as AnyOperation,
|
|
70
|
+
note: "Also returns content_hash, a hash of that markdown. Hand it back to update_page as expect_hash and the update only lands while the page still reads as it did for you.",
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// Derived from update_page's own fields, so the three render and parse through galaxy-ops' zod
|
|
74
|
+
// and carry the same types the next galaxy-ops gives them; only the words are Orbit's.
|
|
75
|
+
const updatePageInput = {
|
|
76
|
+
...updatePageOp.input,
|
|
77
|
+
sectionHeading: updatePageOp.input.title.describe(
|
|
78
|
+
"Heading line of the section to rewrite, exactly as the page has it (for example '## Results'). The section runs to the next heading of the same or a higher level, so its subsections go with it; a heading the page repeats is refused, so name the unique heading above it. Markdown pages only; send section_content with it and leave content out.",
|
|
79
|
+
),
|
|
80
|
+
sectionContent: updatePageOp.input.content.describe(
|
|
81
|
+
"Everything the section should hold, its heading line first and its subsections included. Without any subsection heading it rewrites only the text above the section's first subsection and keeps the subsections. When the page has no section under that heading, this goes on the end.",
|
|
82
|
+
),
|
|
83
|
+
expectHash: updatePageOp.input.content.describe(
|
|
84
|
+
"The content_hash get_page gave you. If the page has been edited after that, nothing is written and you are told to read it again.",
|
|
85
|
+
),
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
type UpdatePageInput = InputOf<typeof updatePageInput>;
|
|
89
|
+
|
|
90
|
+
function refusal(i: UpdatePageInput): string | undefined {
|
|
91
|
+
const section = i.sectionHeading != null || i.sectionContent != null;
|
|
92
|
+
if (section && (i.sectionHeading == null || i.sectionContent == null)) {
|
|
93
|
+
return "section_heading and section_content are a pair: give both to rewrite one section.";
|
|
94
|
+
}
|
|
95
|
+
if (section && i.content != null) {
|
|
96
|
+
return "Give content to replace the whole page, or section_heading with section_content to rewrite one section -- not both.";
|
|
97
|
+
}
|
|
98
|
+
const found = malformedObjectIds(section ? i.sectionContent : i.content);
|
|
99
|
+
return found.length > 0 ? malformedIdsRefusal(found) : undefined;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const updatePageBridgeOp: Operation<
|
|
103
|
+
typeof updatePageInput,
|
|
104
|
+
WithHash<Omit<PageDetail, "content">>
|
|
105
|
+
> = {
|
|
106
|
+
...updatePageBase,
|
|
107
|
+
input: updatePageInput,
|
|
108
|
+
async run(i, ctx) {
|
|
109
|
+
const refused = refusal(i);
|
|
110
|
+
if (refused) throw new GalaxyValidationError(refused);
|
|
111
|
+
let content = i.content;
|
|
112
|
+
if (i.sectionHeading != null || i.expectHash != null) {
|
|
113
|
+
const { data, error, response } = await ctx.client.GET("/api/pages/{id}", {
|
|
114
|
+
params: { path: { id: i.pageId } },
|
|
115
|
+
});
|
|
116
|
+
if (error || !data) throw httpError(response, error);
|
|
117
|
+
const actual = pageContentHash(pageBody(data));
|
|
118
|
+
if (i.expectHash != null && i.expectHash !== actual) {
|
|
119
|
+
throw new GalaxyValidationError(
|
|
120
|
+
`Page '${i.pageId}' changed since it was read: its content_hash is now ${actual}, not ${i.expectHash}. Read it again with get_page and make the edit against what is there now.`,
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
if (i.sectionHeading != null) {
|
|
124
|
+
if (data.content_format !== "markdown") {
|
|
125
|
+
throw new GalaxyValidationError(
|
|
126
|
+
`Page '${i.pageId}' is written in ${data.content_format}, and only a markdown page can have one section rewritten. Send the whole content instead.`,
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
const repeated = repeatedHeadingRefusal(pageBody(data), i.sectionHeading);
|
|
130
|
+
if (repeated) throw new GalaxyValidationError(repeated);
|
|
131
|
+
content = applySectionEdit(pageBody(data), i.sectionHeading, i.sectionContent ?? "");
|
|
132
|
+
}
|
|
133
|
+
// The read can outlive the deadline on a transport that ignores its signal; by then the
|
|
134
|
+
// caller has reported an unknown result and released the record lock, so the write must
|
|
135
|
+
// not go out.
|
|
136
|
+
ctx.signal?.throwIfAborted();
|
|
137
|
+
}
|
|
138
|
+
// No content_format: a page keeps the format it was written in, as galaxy-ops' own
|
|
139
|
+
// update_page leaves it. galaxy-pages-api's updatePage always sends one, which would
|
|
140
|
+
// relabel an HTML page as markdown.
|
|
141
|
+
const body: { edit_source: string; content?: string; title?: string } = {
|
|
142
|
+
edit_source: "agent",
|
|
143
|
+
};
|
|
144
|
+
if (content != null) body.content = content;
|
|
145
|
+
if (i.title != null) body.title = i.title;
|
|
146
|
+
const { data, error, response } = await ctx.client.PUT("/api/pages/{id}", {
|
|
147
|
+
params: { path: { id: i.pageId } },
|
|
148
|
+
body,
|
|
149
|
+
});
|
|
150
|
+
if (error || !data) throw httpError(response, error);
|
|
151
|
+
const written = data as PageDetail;
|
|
152
|
+
return { ...withoutRendered(written), content_hash: pageContentHash(pageBody(written)) };
|
|
153
|
+
},
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
export const updatePageBridge: GalaxyBridge = {
|
|
157
|
+
op: updatePageBridgeOp as unknown as AnyOperation,
|
|
158
|
+
note: "To rewrite one section rather than the whole page, send section_heading and section_content instead of content. With expect_hash set to the content_hash get_page gave you, nothing is written if someone has edited the page in between. The reply carries the written page and its new content_hash.",
|
|
159
|
+
};
|
|
@@ -0,0 +1,417 @@
|
|
|
1
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
parsePageSyncMode,
|
|
4
|
+
pageSlugForHistory,
|
|
5
|
+
pageTitleForHistory,
|
|
6
|
+
strippedNotebookBody,
|
|
7
|
+
hasBodyChanged,
|
|
8
|
+
createPageSyncEngine,
|
|
9
|
+
type PageSyncDeps,
|
|
10
|
+
} from "./galaxy-page-sync.js";
|
|
11
|
+
|
|
12
|
+
describe("parsePageSyncMode", () => {
|
|
13
|
+
it("is auto only for the exact 'auto' value", () => {
|
|
14
|
+
expect(parsePageSyncMode({ LOOM_GALAXY_PAGE_SYNC: "auto" })).toBe("auto");
|
|
15
|
+
expect(parsePageSyncMode({ LOOM_GALAXY_PAGE_SYNC: "1" })).toBe("off");
|
|
16
|
+
expect(parsePageSyncMode({})).toBe("off");
|
|
17
|
+
});
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
describe("pageSlugForHistory / pageTitleForHistory", () => {
|
|
21
|
+
it("derives a stable per-history slug", () => {
|
|
22
|
+
expect(pageSlugForHistory("abc123")).toBe("orbit-abc123");
|
|
23
|
+
});
|
|
24
|
+
it("derives a readable title", () => {
|
|
25
|
+
expect(pageTitleForHistory("abc12345xyz")).toContain("abc12345");
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe("strippedNotebookBody / hasBodyChanged", () => {
|
|
30
|
+
it("removes the binding block and untrusted markers", () => {
|
|
31
|
+
const content = ["# Notebook", "body line", "```loom-galaxy-page", "page_id: p1", "```"].join(
|
|
32
|
+
"\n",
|
|
33
|
+
);
|
|
34
|
+
const stripped = strippedNotebookBody(content);
|
|
35
|
+
expect(stripped).toContain("body line");
|
|
36
|
+
expect(stripped).not.toContain("loom-galaxy-page");
|
|
37
|
+
expect(stripped).not.toContain("page_id");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it("treats identical stripped bodies as unchanged (breaks self-trigger loop)", () => {
|
|
41
|
+
expect(hasBodyChanged("same", "same")).toBe(false);
|
|
42
|
+
expect(hasBodyChanged("old", "new")).toBe(true);
|
|
43
|
+
expect(hasBodyChanged(null, "first")).toBe(true);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
function makeDeps(over: Partial<PageSyncDeps> = {}): PageSyncDeps & {
|
|
48
|
+
pushes: Array<{ historyId: string; slug: string; title: string }>;
|
|
49
|
+
fire: () => void;
|
|
50
|
+
} {
|
|
51
|
+
let cb: (() => void) | null = null;
|
|
52
|
+
const pushes: Array<{ historyId: string; slug: string; title: string }> = [];
|
|
53
|
+
let body = "first";
|
|
54
|
+
const deps: PageSyncDeps & { pushes: typeof pushes; fire: () => void } = {
|
|
55
|
+
mode: "auto",
|
|
56
|
+
hasGalaxy: () => true,
|
|
57
|
+
getHistoryId: async () => "h1",
|
|
58
|
+
readBody: async () => body,
|
|
59
|
+
findPageId: vi.fn(async () => "page-h1"),
|
|
60
|
+
resume: vi.fn(async () => {}),
|
|
61
|
+
push: vi.fn(async (o) => {
|
|
62
|
+
pushes.push(o);
|
|
63
|
+
}),
|
|
64
|
+
subscribe: (fn) => {
|
|
65
|
+
cb = fn;
|
|
66
|
+
return () => {
|
|
67
|
+
cb = null;
|
|
68
|
+
};
|
|
69
|
+
},
|
|
70
|
+
debounceMs: 100,
|
|
71
|
+
pushes,
|
|
72
|
+
fire: () => cb?.(),
|
|
73
|
+
...over,
|
|
74
|
+
};
|
|
75
|
+
// let tests mutate the body the watcher will read
|
|
76
|
+
(deps as unknown as { setBody: (b: string) => void }).setBody = (b: string) => {
|
|
77
|
+
body = b;
|
|
78
|
+
};
|
|
79
|
+
return deps;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
describe("createPageSyncEngine", () => {
|
|
83
|
+
beforeEach(() => vi.useFakeTimers());
|
|
84
|
+
afterEach(() => vi.useRealTimers());
|
|
85
|
+
|
|
86
|
+
it("resolves the page id by slug and resumes by id", async () => {
|
|
87
|
+
const deps = makeDeps();
|
|
88
|
+
const engine = createPageSyncEngine(deps);
|
|
89
|
+
await engine.init();
|
|
90
|
+
expect(deps.findPageId).toHaveBeenCalledWith("h1", "orbit-h1");
|
|
91
|
+
expect(deps.resume).toHaveBeenCalledWith("page-h1");
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it("does not resume when no page exists for the history (fresh)", async () => {
|
|
95
|
+
const deps = makeDeps({ findPageId: vi.fn(async () => null) });
|
|
96
|
+
const engine = createPageSyncEngine(deps);
|
|
97
|
+
await engine.init();
|
|
98
|
+
expect(deps.resume).not.toHaveBeenCalled();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
it("is a no-op when mode is off", async () => {
|
|
102
|
+
const deps = makeDeps({ mode: "off" });
|
|
103
|
+
const engine = createPageSyncEngine(deps);
|
|
104
|
+
await engine.init();
|
|
105
|
+
expect(deps.resume).not.toHaveBeenCalled();
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it("debounce-pushes a changed body once", async () => {
|
|
109
|
+
const deps = makeDeps();
|
|
110
|
+
const engine = createPageSyncEngine(deps);
|
|
111
|
+
await engine.init();
|
|
112
|
+
(deps as unknown as { setBody: (b: string) => void }).setBody("changed");
|
|
113
|
+
deps.fire();
|
|
114
|
+
deps.fire();
|
|
115
|
+
await vi.advanceTimersByTimeAsync(150);
|
|
116
|
+
expect(deps.pushes).toHaveLength(1);
|
|
117
|
+
expect(deps.pushes[0]).toEqual({
|
|
118
|
+
historyId: "h1",
|
|
119
|
+
slug: "orbit-h1",
|
|
120
|
+
title: expect.any(String),
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("skips a push when the stripped body is unchanged (self-trigger guard)", async () => {
|
|
125
|
+
const deps = makeDeps();
|
|
126
|
+
const engine = createPageSyncEngine(deps);
|
|
127
|
+
await engine.init(); // body == "first" recorded as last-pushed baseline
|
|
128
|
+
deps.fire(); // body still "first"
|
|
129
|
+
await vi.advanceTimersByTimeAsync(150);
|
|
130
|
+
expect(deps.pushes).toHaveLength(0);
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("flush() pushes the latest body immediately", async () => {
|
|
134
|
+
const deps = makeDeps();
|
|
135
|
+
const engine = createPageSyncEngine(deps);
|
|
136
|
+
await engine.init();
|
|
137
|
+
(deps as unknown as { setBody: (b: string) => void }).setBody("final");
|
|
138
|
+
await engine.flush();
|
|
139
|
+
expect(deps.pushes).toHaveLength(1);
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
it("serializes pushes: flush coalesces with an in-flight push (no concurrent overlap)", async () => {
|
|
143
|
+
let releasePush: () => void = () => {};
|
|
144
|
+
let calls = 0;
|
|
145
|
+
const deps = makeDeps({
|
|
146
|
+
push: vi.fn(async () => {
|
|
147
|
+
calls += 1;
|
|
148
|
+
await new Promise<void>((resolve) => {
|
|
149
|
+
releasePush = () => resolve();
|
|
150
|
+
});
|
|
151
|
+
}),
|
|
152
|
+
});
|
|
153
|
+
const engine = createPageSyncEngine(deps);
|
|
154
|
+
await engine.init(); // lastBody baseline = "first"
|
|
155
|
+
(deps as unknown as { setBody: (b: string) => void }).setBody("changed");
|
|
156
|
+
deps.fire();
|
|
157
|
+
await vi.advanceTimersByTimeAsync(150); // debounce fires -> push #1 starts and hangs
|
|
158
|
+
expect(calls).toBe(1);
|
|
159
|
+
// The body is unchanged since push #1 started; flush must await the in-flight
|
|
160
|
+
// push, not launch a second concurrent one (the pre-fix code did, because
|
|
161
|
+
// lastBody is only set after the await).
|
|
162
|
+
const flushP = engine.flush();
|
|
163
|
+
releasePush(); // let push #1 complete
|
|
164
|
+
await flushP;
|
|
165
|
+
expect(calls).toBe(1);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it("does not throw when getHistoryId rejects (fail-open hardening)", async () => {
|
|
169
|
+
const deps = makeDeps({
|
|
170
|
+
getHistoryId: async () => {
|
|
171
|
+
throw new Error("404");
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
const engine = createPageSyncEngine(deps);
|
|
175
|
+
await expect(engine.init()).resolves.toBeUndefined();
|
|
176
|
+
expect(deps.resume).not.toHaveBeenCalled();
|
|
177
|
+
expect(deps.pushes).toHaveLength(0);
|
|
178
|
+
});
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// #330 (Codex, HIGH): page-sync failures were silent AND permanent. A Galaxy
|
|
182
|
+
// that was briefly down at launch left init() with no history, which disabled
|
|
183
|
+
// sync for the entire session while the UI still showed Galaxy as connected --
|
|
184
|
+
// the notebook then only ever existed under /tmp, and the container's /tmp dies
|
|
185
|
+
// with the job.
|
|
186
|
+
describe("createPageSyncEngine recovery", () => {
|
|
187
|
+
beforeEach(() => vi.useFakeTimers());
|
|
188
|
+
afterEach(() => vi.useRealTimers());
|
|
189
|
+
|
|
190
|
+
// Cooldown is rate-limiting, not a budget: pass now() so tests control it.
|
|
191
|
+
function makeClock() {
|
|
192
|
+
let t = 0;
|
|
193
|
+
return { now: () => t, advance: (ms: number) => (t += ms) };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
it("re-arms on a later change when the history lookup failed at launch", async () => {
|
|
197
|
+
const clock = makeClock();
|
|
198
|
+
let attempts = 0;
|
|
199
|
+
const deps = makeDeps({
|
|
200
|
+
getHistoryId: async () => {
|
|
201
|
+
attempts++;
|
|
202
|
+
if (attempts === 1) throw new Error("galaxy down");
|
|
203
|
+
return "h1";
|
|
204
|
+
},
|
|
205
|
+
findPageId: vi.fn(async () => null), // no prior page: local notebook is authoritative
|
|
206
|
+
now: clock.now,
|
|
207
|
+
retryCooldownMs: 1000,
|
|
208
|
+
});
|
|
209
|
+
const engine = createPageSyncEngine(deps);
|
|
210
|
+
await engine.init();
|
|
211
|
+
expect(deps.pushes).toHaveLength(0);
|
|
212
|
+
|
|
213
|
+
clock.advance(2000); // past the cooldown
|
|
214
|
+
(deps as unknown as { setBody: (b: string) => void }).setBody("written while galaxy was down");
|
|
215
|
+
deps.fire();
|
|
216
|
+
await vi.advanceTimersByTimeAsync(150);
|
|
217
|
+
|
|
218
|
+
expect(attempts).toBe(2);
|
|
219
|
+
expect(deps.pushes).toHaveLength(1);
|
|
220
|
+
expect(deps.pushes[0].historyId).toBe("h1");
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it("recovers content written during the outage on flush, not just on change", async () => {
|
|
224
|
+
let attempts = 0;
|
|
225
|
+
const deps = makeDeps({
|
|
226
|
+
getHistoryId: async () => {
|
|
227
|
+
attempts++;
|
|
228
|
+
if (attempts === 1) return null; // no history yet
|
|
229
|
+
return "h1";
|
|
230
|
+
},
|
|
231
|
+
findPageId: vi.fn(async () => null),
|
|
232
|
+
now: makeClock().now, // clock never advances: flush must force past the cooldown
|
|
233
|
+
});
|
|
234
|
+
const engine = createPageSyncEngine(deps);
|
|
235
|
+
await engine.init();
|
|
236
|
+
(deps as unknown as { setBody: (b: string) => void }).setBody("work worth keeping");
|
|
237
|
+
|
|
238
|
+
await engine.flush();
|
|
239
|
+
|
|
240
|
+
// Without arming inside the push path this flushed nothing and the work was
|
|
241
|
+
// lost with the container.
|
|
242
|
+
expect(deps.pushes).toHaveLength(1);
|
|
243
|
+
expect(deps.pushes[0].historyId).toBe("h1");
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// Codex #9: a hard attempt cap burned its whole budget on a few debounced edits
|
|
247
|
+
// during one short outage, then never synced again -- the exact silent data loss
|
|
248
|
+
// the retry was added to prevent. Rate-limit instead of counting.
|
|
249
|
+
it("keeps retrying after many failures, once the cooldown passes", async () => {
|
|
250
|
+
const clock = makeClock();
|
|
251
|
+
let attempts = 0;
|
|
252
|
+
const deps = makeDeps({
|
|
253
|
+
getHistoryId: async () => {
|
|
254
|
+
attempts++;
|
|
255
|
+
if (attempts <= 8) throw new Error("galaxy down");
|
|
256
|
+
return "h1";
|
|
257
|
+
},
|
|
258
|
+
findPageId: vi.fn(async () => null),
|
|
259
|
+
now: clock.now,
|
|
260
|
+
retryCooldownMs: 1000,
|
|
261
|
+
});
|
|
262
|
+
const engine = createPageSyncEngine(deps);
|
|
263
|
+
await engine.init();
|
|
264
|
+
for (let i = 0; i < 8; i++) {
|
|
265
|
+
clock.advance(1500);
|
|
266
|
+
(deps as unknown as { setBody: (b: string) => void }).setBody(`body-${i}`);
|
|
267
|
+
deps.fire();
|
|
268
|
+
await vi.advanceTimersByTimeAsync(150);
|
|
269
|
+
}
|
|
270
|
+
expect(deps.pushes).toHaveLength(1); // recovered on the 9th attempt
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
it("rate-limits retries so a dead Galaxy isn't hit on every keystroke", async () => {
|
|
274
|
+
const clock = makeClock();
|
|
275
|
+
let attempts = 0;
|
|
276
|
+
const deps = makeDeps({
|
|
277
|
+
getHistoryId: async () => {
|
|
278
|
+
attempts++;
|
|
279
|
+
throw new Error("galaxy down");
|
|
280
|
+
},
|
|
281
|
+
now: clock.now,
|
|
282
|
+
retryCooldownMs: 30000,
|
|
283
|
+
});
|
|
284
|
+
const engine = createPageSyncEngine(deps);
|
|
285
|
+
await engine.init(); // attempt 1
|
|
286
|
+
for (let i = 0; i < 6; i++) {
|
|
287
|
+
clock.advance(100); // well inside the cooldown
|
|
288
|
+
(deps as unknown as { setBody: (b: string) => void }).setBody(`body-${i}`);
|
|
289
|
+
deps.fire();
|
|
290
|
+
await vi.advanceTimersByTimeAsync(150);
|
|
291
|
+
}
|
|
292
|
+
expect(attempts).toBe(1);
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it("times out a hanging history request instead of stalling session_start", async () => {
|
|
296
|
+
const deps = makeDeps({
|
|
297
|
+
getHistoryId: () => new Promise<string>(() => {}), // never settles
|
|
298
|
+
initTimeoutMs: 5000,
|
|
299
|
+
});
|
|
300
|
+
const engine = createPageSyncEngine(deps);
|
|
301
|
+
const done = engine.init();
|
|
302
|
+
await vi.advanceTimersByTimeAsync(5001);
|
|
303
|
+
await expect(done).resolves.toBeUndefined();
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
// Codex #4: history and listing were timed out but resume wasn't, so a page GET
|
|
307
|
+
// that accepted the connection and never answered still hung session_start.
|
|
308
|
+
it("times out a hanging page resume", async () => {
|
|
309
|
+
const deps = makeDeps({
|
|
310
|
+
resume: vi.fn(() => new Promise<void>(() => {})), // never settles
|
|
311
|
+
initTimeoutMs: 5000,
|
|
312
|
+
});
|
|
313
|
+
const engine = createPageSyncEngine(deps);
|
|
314
|
+
const done = engine.init();
|
|
315
|
+
await vi.advanceTimersByTimeAsync(5001);
|
|
316
|
+
await expect(done).resolves.toBeUndefined();
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it("does not publish a page for an untouched notebook", async () => {
|
|
320
|
+
const deps = makeDeps({ findPageId: vi.fn(async () => null) });
|
|
321
|
+
(deps as unknown as { setBody: (b: string) => void }).setBody("");
|
|
322
|
+
const engine = createPageSyncEngine(deps);
|
|
323
|
+
await engine.init();
|
|
324
|
+
deps.fire();
|
|
325
|
+
await vi.advanceTimersByTimeAsync(150);
|
|
326
|
+
await engine.flush();
|
|
327
|
+
expect(deps.pushes).toHaveLength(0);
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// Codex #5: skipping every empty body meant a user who deliberately cleared the
|
|
331
|
+
// notebook could never sync that -- Galaxy kept the stale content and restored
|
|
332
|
+
// it on the next launch.
|
|
333
|
+
it("pushes a deliberate clear of a resumed page", async () => {
|
|
334
|
+
const deps = makeDeps(); // resumes page-h1, baseline "first"
|
|
335
|
+
const engine = createPageSyncEngine(deps);
|
|
336
|
+
await engine.init();
|
|
337
|
+
(deps as unknown as { setBody: (b: string) => void }).setBody("");
|
|
338
|
+
deps.fire();
|
|
339
|
+
await vi.advanceTimersByTimeAsync(150);
|
|
340
|
+
expect(deps.pushes).toHaveLength(1);
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
it("still skips a redundant push right after resuming an existing page", async () => {
|
|
344
|
+
// resume() rewrites the notebook, which fires the watcher; that self-trigger
|
|
345
|
+
// must not bounce straight back to Galaxy as a no-op push.
|
|
346
|
+
const deps = makeDeps(); // findPageId -> "page-h1", body stays "first"
|
|
347
|
+
const engine = createPageSyncEngine(deps);
|
|
348
|
+
await engine.init();
|
|
349
|
+
deps.fire();
|
|
350
|
+
await vi.advanceTimersByTimeAsync(150);
|
|
351
|
+
expect(deps.pushes).toHaveLength(0);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
// Codex #6: historyId was committed before page discovery, so one failed
|
|
355
|
+
// listing left the engine armed but blind to an existing page -- the next push
|
|
356
|
+
// took the create path and duplicated it, and discovery never re-ran.
|
|
357
|
+
it("stays unarmed when page discovery fails, then adopts the page on retry", async () => {
|
|
358
|
+
const clock = makeClock();
|
|
359
|
+
let lists = 0;
|
|
360
|
+
const deps = makeDeps({
|
|
361
|
+
findPageId: vi.fn(async () => {
|
|
362
|
+
lists++;
|
|
363
|
+
if (lists === 1) throw new Error("503");
|
|
364
|
+
return "page-h1";
|
|
365
|
+
}),
|
|
366
|
+
now: clock.now,
|
|
367
|
+
retryCooldownMs: 1000,
|
|
368
|
+
});
|
|
369
|
+
const engine = createPageSyncEngine(deps);
|
|
370
|
+
await engine.init();
|
|
371
|
+
expect(deps.resume).not.toHaveBeenCalled();
|
|
372
|
+
|
|
373
|
+
(deps as unknown as { setBody: (b: string) => void }).setBody("edited");
|
|
374
|
+
deps.fire();
|
|
375
|
+
await vi.advanceTimersByTimeAsync(150);
|
|
376
|
+
// Cooldown still running: must not create a rival page for the same history.
|
|
377
|
+
expect(deps.pushes).toHaveLength(0);
|
|
378
|
+
|
|
379
|
+
clock.advance(2000);
|
|
380
|
+
deps.fire();
|
|
381
|
+
await vi.advanceTimersByTimeAsync(150);
|
|
382
|
+
expect(deps.resume).toHaveBeenCalledWith("page-h1"); // adopted, not duplicated
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it("stays unarmed when resume of an existing page fails", async () => {
|
|
386
|
+
const deps = makeDeps({
|
|
387
|
+
resume: vi.fn(async () => {
|
|
388
|
+
throw new Error("boom");
|
|
389
|
+
}),
|
|
390
|
+
});
|
|
391
|
+
const engine = createPageSyncEngine(deps);
|
|
392
|
+
await engine.init();
|
|
393
|
+
(deps as unknown as { setBody: (b: string) => void }).setBody("edited");
|
|
394
|
+
deps.fire();
|
|
395
|
+
await vi.advanceTimersByTimeAsync(150);
|
|
396
|
+
// Pushing here would overwrite a page we failed to read.
|
|
397
|
+
expect(deps.pushes).toHaveLength(0);
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
// Codex #3: an arm() in flight when the engine is disposed used to land anyway,
|
|
401
|
+
// and resume() resolves the notebook path globally -- so a stale engine could
|
|
402
|
+
// overwrite the NEXT session's notebook with its own page.
|
|
403
|
+
it("does not resume after dispose", async () => {
|
|
404
|
+
let release: (v: string) => void = () => {};
|
|
405
|
+
const deps = makeDeps({
|
|
406
|
+
getHistoryId: () => new Promise<string>((r) => (release = r)),
|
|
407
|
+
});
|
|
408
|
+
const engine = createPageSyncEngine(deps);
|
|
409
|
+
const done = engine.init();
|
|
410
|
+
engine.dispose();
|
|
411
|
+
release("h1");
|
|
412
|
+
await done;
|
|
413
|
+
await vi.advanceTimersByTimeAsync(50);
|
|
414
|
+
expect(deps.resume).not.toHaveBeenCalled();
|
|
415
|
+
expect(deps.pushes).toHaveLength(0);
|
|
416
|
+
});
|
|
417
|
+
});
|