@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,53 @@
|
|
|
1
|
+
// Fence names for the typed blocks Orbit keeps in notebook.md. Shared so the
|
|
2
|
+
// brain and the Orbit renderer agree on what counts as one of our blocks.
|
|
3
|
+
//
|
|
4
|
+
// The product is being renamed from Loom to Orbit, and notebooks outlive any
|
|
5
|
+
// one release: a CLI and a bundled desktop app can be different versions
|
|
6
|
+
// pointed at the same notebook. So readers accept every prefix here forever,
|
|
7
|
+
// while writers use exactly one. Flipping NOTEBOOK_FENCE_WRITE_PREFIX is the
|
|
8
|
+
// whole write-side switch; upserts rewrite a matched block under it, so a
|
|
9
|
+
// mixed notebook converges instead of growing duplicates.
|
|
10
|
+
|
|
11
|
+
export const NOTEBOOK_FENCE_WRITE_PREFIX = "orbit";
|
|
12
|
+
|
|
13
|
+
export const NOTEBOOK_FENCE_READ_PREFIXES = Object.freeze(["loom", "orbit"]);
|
|
14
|
+
|
|
15
|
+
/** @typedef {"session" | "invocation" | "job" | "galaxy-page" | "udt" | "proposal"} NotebookFenceKind */
|
|
16
|
+
|
|
17
|
+
/** @param {NotebookFenceKind} kind @returns {string} */
|
|
18
|
+
export function notebookFenceOpen(kind) {
|
|
19
|
+
return "```" + NOTEBOOK_FENCE_WRITE_PREFIX + "-" + kind;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** @param {string} line @param {NotebookFenceKind} kind @returns {boolean} */
|
|
23
|
+
export function isNotebookFenceOpen(line, kind) {
|
|
24
|
+
const trimmed = line.trim();
|
|
25
|
+
return NOTEBOOK_FENCE_READ_PREFIXES.some((p) => trimmed === "```" + p + "-" + kind);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Replace every block in `ranges` with `newBlock`, written where the first one
|
|
30
|
+
* sat. Two blocks sharing a key can only come from a writer that didn't
|
|
31
|
+
* recognise the other prefix, so collapsing them is what keeps the key unique.
|
|
32
|
+
*
|
|
33
|
+
* @param {string} content
|
|
34
|
+
* @param {ReadonlyArray<{ start: number; end: number }>} ranges line ranges, inclusive of both fences
|
|
35
|
+
* @param {string[]} newBlock
|
|
36
|
+
* @returns {string}
|
|
37
|
+
*/
|
|
38
|
+
export function replaceNotebookBlocks(content, ranges, newBlock) {
|
|
39
|
+
const lines = content.split("\n");
|
|
40
|
+
if (ranges.length === 0) return content;
|
|
41
|
+
const drop = new Set();
|
|
42
|
+
for (const r of ranges) {
|
|
43
|
+
for (let li = r.start; li <= r.end; li++) drop.add(li);
|
|
44
|
+
}
|
|
45
|
+
const insertAt = Math.min(...ranges.map((r) => r.start));
|
|
46
|
+
const rebuilt = [];
|
|
47
|
+
for (let li = 0; li < lines.length; li++) {
|
|
48
|
+
if (li === insertAt) rebuilt.push(...newBlock);
|
|
49
|
+
if (drop.has(li)) continue;
|
|
50
|
+
rebuilt.push(lines[li]);
|
|
51
|
+
}
|
|
52
|
+
return rebuilt.join("\n");
|
|
53
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
export declare const OBSERVATION_SCHEMA_VERSION: 1;
|
|
2
|
+
export declare const OBSERVATIONS_ROUTE: "/observations";
|
|
3
|
+
export declare const OBSERVATION_KEY_HEADER: "X-Orbit-Feedback-Key";
|
|
4
|
+
export declare const RETRACT_TOKEN_HEADER: "X-Retract-Token";
|
|
5
|
+
export declare const OBSERVATIONS_ENDPOINT_URL: string;
|
|
6
|
+
export declare const OBSERVATION_MAX_BYTES: number;
|
|
7
|
+
|
|
8
|
+
export declare const PUBLIC_GALAXY_SERVERS: readonly string[];
|
|
9
|
+
export declare const PRIVATE_SERVER: "private";
|
|
10
|
+
|
|
11
|
+
export type ObservationKind =
|
|
12
|
+
| "tool-error"
|
|
13
|
+
| "retry-loop"
|
|
14
|
+
| "assertion-failed"
|
|
15
|
+
| "user-correction"
|
|
16
|
+
| "silent-wrong-result"
|
|
17
|
+
| "other";
|
|
18
|
+
export type ObservationStage =
|
|
19
|
+
| "data-acquisition"
|
|
20
|
+
| "metadata-reconciliation"
|
|
21
|
+
| "tool-parameterization"
|
|
22
|
+
| "job-execution"
|
|
23
|
+
| "result-interpretation"
|
|
24
|
+
| "unknown";
|
|
25
|
+
export type ObservationTrigger =
|
|
26
|
+
"tool_error" | "retry_loop" | "assertion" | "user_correction" | "explicit";
|
|
27
|
+
export type ObservationApp = "orbit" | "loom-cli";
|
|
28
|
+
export type ObservationPlatform = "darwin" | "linux" | "win32";
|
|
29
|
+
|
|
30
|
+
export declare const OBSERVATION_KINDS: readonly ObservationKind[];
|
|
31
|
+
export declare const OBSERVATION_STAGES: readonly ObservationStage[];
|
|
32
|
+
export declare const OBSERVATION_TRIGGERS: readonly ObservationTrigger[];
|
|
33
|
+
export declare const OBSERVATION_APPS: readonly ObservationApp[];
|
|
34
|
+
export declare const OBSERVATION_PLATFORMS: readonly ObservationPlatform[];
|
|
35
|
+
|
|
36
|
+
export declare const SIGNATURE_MAX: number;
|
|
37
|
+
export declare const DESCRIPTION_MAX: number;
|
|
38
|
+
export declare const TOOLS_MAX: number;
|
|
39
|
+
export declare const TOOL_ID_MAX: number;
|
|
40
|
+
export declare const TOOL_VERSION_MAX: number;
|
|
41
|
+
export declare const MCP_TOOL_MAX: number;
|
|
42
|
+
export declare const DATATYPES_MAX: number;
|
|
43
|
+
export declare const DATATYPE_MAX: number;
|
|
44
|
+
export declare const VERSION_MAX: number;
|
|
45
|
+
export declare const UNKNOWN_SIGNATURE: "unknown";
|
|
46
|
+
|
|
47
|
+
export interface ObservationToolRef {
|
|
48
|
+
/** Galaxy tool id, version segment split off when the id is a toolshed path. */
|
|
49
|
+
id: string;
|
|
50
|
+
version?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface Observation {
|
|
54
|
+
schemaVersion: 1;
|
|
55
|
+
/** Client-generated UUID v4. */
|
|
56
|
+
id: string;
|
|
57
|
+
clientTs: string;
|
|
58
|
+
client: {
|
|
59
|
+
app: ObservationApp;
|
|
60
|
+
version: string;
|
|
61
|
+
platform: ObservationPlatform;
|
|
62
|
+
/** Present and true only under WSL; a WSL install reports platform "linux". */
|
|
63
|
+
wsl?: boolean;
|
|
64
|
+
};
|
|
65
|
+
/** 32 lowercase hex, random, generated once per install. */
|
|
66
|
+
installToken: string;
|
|
67
|
+
kind: ObservationKind;
|
|
68
|
+
stage: ObservationStage;
|
|
69
|
+
trigger: ObservationTrigger;
|
|
70
|
+
tools: ObservationToolRef[];
|
|
71
|
+
mcpTool?: string;
|
|
72
|
+
datatypes: string[];
|
|
73
|
+
/** Normalized error/outcome signature; see normalizeSignature. */
|
|
74
|
+
signature: string;
|
|
75
|
+
galaxy: { version?: string; server: string };
|
|
76
|
+
/** Agent- or user-written, generic, validator-checked. May be empty. */
|
|
77
|
+
description: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export declare function rawSignatureLine(text: unknown): string;
|
|
81
|
+
export declare function signatureStageLeaks(text: unknown): string[];
|
|
82
|
+
export declare function normalizeSignature(text: unknown): string;
|
|
83
|
+
export declare const LEAK_PATTERNS: ReadonlyArray<readonly [string, RegExp]>;
|
|
84
|
+
export declare function validateObservation(
|
|
85
|
+
obj: unknown,
|
|
86
|
+
): { ok: true } | { ok: false; errors: string[] };
|
|
87
|
+
export declare const CLIENT_LEAK_PATTERNS: ReadonlyArray<readonly [string, RegExp]>;
|
|
88
|
+
export declare function textLeaks(text: unknown): string[];
|
|
89
|
+
export declare function scanObservationForLeaks(obs: unknown): string[];
|
|
90
|
+
export declare function capObservation(obs: unknown): Observation;
|
|
91
|
+
export declare function observationByteLength(obs: unknown): number;
|
|
@@ -0,0 +1,544 @@
|
|
|
1
|
+
// Shared observation wire contract (community knowledge loop, contract C1).
|
|
2
|
+
// Dual-file (.js runtime + .d.ts types) to match feedback-contract: the brain
|
|
3
|
+
// resolves a real .js at runtime, so a single .ts would risk a missing runtime
|
|
4
|
+
// file. No Node imports here -- this file is renderer-safe and is shared by the
|
|
5
|
+
// brain, Orbit, and (by verbatim copy) the orbit-feedback Worker.
|
|
6
|
+
//
|
|
7
|
+
// normalizeSignature, LEAK_PATTERNS and validateObservation are security
|
|
8
|
+
// controls, not formatting helpers. They fail closed: a payload that trips a
|
|
9
|
+
// leak pattern is dropped, never "cleaned up and sent anyway". The Worker keeps
|
|
10
|
+
// a byte-compatible copy of the same regexes, so a change here is a change
|
|
11
|
+
// there.
|
|
12
|
+
|
|
13
|
+
export const OBSERVATION_SCHEMA_VERSION = 1;
|
|
14
|
+
export const OBSERVATIONS_ROUTE = "/observations";
|
|
15
|
+
// Same shared key as /feedback -- one secret per install, two routes.
|
|
16
|
+
export const OBSERVATION_KEY_HEADER = "X-Orbit-Feedback-Key";
|
|
17
|
+
export const RETRACT_TOKEN_HEADER = "X-Retract-Token";
|
|
18
|
+
export const OBSERVATIONS_ENDPOINT_URL = "https://orbit-feedback.dannon-baker.workers.dev";
|
|
19
|
+
// The Worker's body cap. Enforced client-side too so a doomed POST is never sent.
|
|
20
|
+
export const OBSERVATION_MAX_BYTES = 16 * 1024;
|
|
21
|
+
|
|
22
|
+
// Contract C7: the single source of truth for the public-server allowlist.
|
|
23
|
+
// Anything else is "private" -- an exact hostname match, never a suffix match,
|
|
24
|
+
// so an institutional mirror at galaxy.usegalaxy.org.example cannot pass.
|
|
25
|
+
export const PUBLIC_GALAXY_SERVERS = Object.freeze([
|
|
26
|
+
"usegalaxy.org",
|
|
27
|
+
"usegalaxy.eu",
|
|
28
|
+
"usegalaxy.org.au",
|
|
29
|
+
"usegalaxy.fr",
|
|
30
|
+
"usegalaxy.no",
|
|
31
|
+
"usegalaxy.cz",
|
|
32
|
+
"test.galaxyproject.org",
|
|
33
|
+
]);
|
|
34
|
+
export const PRIVATE_SERVER = "private";
|
|
35
|
+
|
|
36
|
+
export const OBSERVATION_KINDS = Object.freeze([
|
|
37
|
+
"tool-error",
|
|
38
|
+
"retry-loop",
|
|
39
|
+
"assertion-failed",
|
|
40
|
+
"user-correction",
|
|
41
|
+
"silent-wrong-result",
|
|
42
|
+
"other",
|
|
43
|
+
]);
|
|
44
|
+
export const OBSERVATION_STAGES = Object.freeze([
|
|
45
|
+
"data-acquisition",
|
|
46
|
+
"metadata-reconciliation",
|
|
47
|
+
"tool-parameterization",
|
|
48
|
+
"job-execution",
|
|
49
|
+
"result-interpretation",
|
|
50
|
+
"unknown",
|
|
51
|
+
]);
|
|
52
|
+
export const OBSERVATION_TRIGGERS = Object.freeze([
|
|
53
|
+
"tool_error",
|
|
54
|
+
"retry_loop",
|
|
55
|
+
"assertion",
|
|
56
|
+
"user_correction",
|
|
57
|
+
"explicit",
|
|
58
|
+
]);
|
|
59
|
+
export const OBSERVATION_APPS = Object.freeze(["orbit", "loom-cli"]);
|
|
60
|
+
export const OBSERVATION_PLATFORMS = Object.freeze(["darwin", "linux", "win32"]);
|
|
61
|
+
|
|
62
|
+
export const SIGNATURE_MAX = 200;
|
|
63
|
+
export const DESCRIPTION_MAX = 500;
|
|
64
|
+
export const TOOLS_MAX = 5;
|
|
65
|
+
export const TOOL_ID_MAX = 200;
|
|
66
|
+
export const TOOL_VERSION_MAX = 40;
|
|
67
|
+
export const MCP_TOOL_MAX = 80;
|
|
68
|
+
export const DATATYPES_MAX = 5;
|
|
69
|
+
export const DATATYPE_MAX = 40;
|
|
70
|
+
export const VERSION_MAX = 40;
|
|
71
|
+
|
|
72
|
+
// `signature` is half the intake route's cluster key, so it is never empty.
|
|
73
|
+
// Normalization can legitimately reduce a line to nothing (a line that was only
|
|
74
|
+
// a path, or only an id); the builder substitutes this instead of shipping a
|
|
75
|
+
// payload the intake route would refuse.
|
|
76
|
+
export const UNKNOWN_SIGNATURE = "unknown";
|
|
77
|
+
|
|
78
|
+
// Locked order. The lesson matcher imports this function; lessons/validate.mjs
|
|
79
|
+
// keeps its own copy (it runs before anything is built), held to this one by
|
|
80
|
+
// tests/observation-signature.test.ts. A change here is a change to the shared
|
|
81
|
+
// contract first.
|
|
82
|
+
//
|
|
83
|
+
// URL before path is the reason the order is written down at all: paths-first
|
|
84
|
+
// turned `https://host/a/b` into `https:<path>`, which left the scheme behind
|
|
85
|
+
// and slipped past the validator's https?:// check.
|
|
86
|
+
//
|
|
87
|
+
// Deliberately NOT here: a rewrite for the crude
|
|
88
|
+
// hid/dataset/history-followed-by-a-number shape, and one for non-ASCII. The
|
|
89
|
+
// validator rejects both, so a line carrying either is DROPPED rather than
|
|
90
|
+
// reshaped. That loses some legitimate signal (a Galaxy message about a
|
|
91
|
+
// two-digit hid, a non-English message) and it is the fail-closed direction.
|
|
92
|
+
const NORMALIZERS = Object.freeze([
|
|
93
|
+
[/https?:\/\/\S+/g, "<url>"],
|
|
94
|
+
[/[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/g, "<email>"],
|
|
95
|
+
// Two separators required, so ordinary prose ("and/or") is not read as a
|
|
96
|
+
// path. A single-segment absolute path like /etc is not identifying.
|
|
97
|
+
[/(?:[A-Za-z]:[\\/]|~[\\/]|\/)[^\s"'`<>|]*[\\/][^\s"'`<>|]*/g, "<path>"],
|
|
98
|
+
[/[0-9a-fA-F]{16,}/g, "<id>"],
|
|
99
|
+
[/\d{5,}/g, "<n>"],
|
|
100
|
+
]);
|
|
101
|
+
|
|
102
|
+
/** The line normalizeSignature reads, before any rewrite or cap. */
|
|
103
|
+
export function rawSignatureLine(text) {
|
|
104
|
+
return String(text ?? "")
|
|
105
|
+
.split(/\r?\n/)[0]
|
|
106
|
+
.replace(/\s+/g, " ")
|
|
107
|
+
.trim();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
export function normalizeSignature(text) {
|
|
111
|
+
let s = rawSignatureLine(text);
|
|
112
|
+
for (const [re, repl] of NORMALIZERS) s = s.replace(re, repl);
|
|
113
|
+
// Plain slice: appending an ellipsis would make the result non-ASCII and the
|
|
114
|
+
// validator would then reject every truncated signature.
|
|
115
|
+
s = s.slice(0, SIGNATURE_MAX).trim();
|
|
116
|
+
// The intake route clusters on (kind, signature), so empty is not a legal
|
|
117
|
+
// value. One owner of the fallback, here rather than in each caller.
|
|
118
|
+
return s || UNKNOWN_SIGNATURE;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Client rules NOT run at the early stage, because the later rewrites remove
|
|
122
|
+
// what they match whole rather than mutilating it: a rooted path becomes
|
|
123
|
+
// <path>, and an id phrase's long number becomes <id> or <n>. A relative path
|
|
124
|
+
// or a short id number survives those rewrites and is caught at the full-table
|
|
125
|
+
// stage. Running them early would withhold nearly every real Galaxy error.
|
|
126
|
+
const EARLY_STAGE_SKIP = new Set(["path-separator", "id-phrase"]);
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* The staged leak scan for a signature, as pattern names (empty when clean).
|
|
130
|
+
* Each shape is scanned while it is still intact, so no rewrite can hide one:
|
|
131
|
+
*
|
|
132
|
+
* 1. apply the <url> and <email> rewrites, which consume a whole token and
|
|
133
|
+
* can't create or hide another shape;
|
|
134
|
+
* 2. run the client table over that text -- the shapes the <path>, <id> and
|
|
135
|
+
* <n> rewrites could erase or mutilate (galaxyprod:12345 -> galaxyprod:<n>,
|
|
136
|
+
* a UUID losing its first block to <n>);
|
|
137
|
+
* 3. apply the rest, then run the full table over the normalized text before
|
|
138
|
+
* the length cap, so a host cut mid-label at the cap is still seen.
|
|
139
|
+
*
|
|
140
|
+
* The final capped value is scanned again with the rest of the payload by
|
|
141
|
+
* scanObservationForLeaks.
|
|
142
|
+
*/
|
|
143
|
+
export function signatureStageLeaks(text) {
|
|
144
|
+
let s = rawSignatureLine(text);
|
|
145
|
+
for (const [re, repl] of NORMALIZERS.slice(0, 2)) s = s.replace(re, repl);
|
|
146
|
+
const hits = [];
|
|
147
|
+
for (const [name, re] of CLIENT_LEAK_PATTERNS) {
|
|
148
|
+
if (!EARLY_STAGE_SKIP.has(name) && re.test(s)) hits.push(name);
|
|
149
|
+
}
|
|
150
|
+
for (const [re, repl] of NORMALIZERS.slice(2)) s = s.replace(re, repl);
|
|
151
|
+
hits.push(...textLeaks(s));
|
|
152
|
+
return [...new Set(hits)];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// The leak table. No `g` flags: `test()` on a global regex is stateful and
|
|
156
|
+
// would skip every other call. Names are the error suffix the validator
|
|
157
|
+
// reports, so a 400 from the Worker still never echoes a value.
|
|
158
|
+
export const LEAK_PATTERNS = Object.freeze([
|
|
159
|
+
Object.freeze(["url", /https?:\/\//i]),
|
|
160
|
+
Object.freeze(["home-path", /\/Users\/|\/home\//i]),
|
|
161
|
+
Object.freeze(["windows-path", /[A-Za-z]:[\\/]/]),
|
|
162
|
+
Object.freeze(["tilde-path", /~[\\/]/]),
|
|
163
|
+
Object.freeze(["long-hex", /[0-9a-fA-F]{16,}/]),
|
|
164
|
+
Object.freeze(["email", /[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}/]),
|
|
165
|
+
// Crude but cheap, per C1: a Galaxy id phrase followed by a number.
|
|
166
|
+
Object.freeze(["galaxy-id-phrase", /\b(?:history|dataset|hid)\b[^A-Za-z0-9]{0,4}\d/i]),
|
|
167
|
+
Object.freeze(["non-ascii", /[^\x20-\x7E]/]),
|
|
168
|
+
]);
|
|
169
|
+
|
|
170
|
+
const TOP_KEYS = new Set([
|
|
171
|
+
"schemaVersion",
|
|
172
|
+
"id",
|
|
173
|
+
"clientTs",
|
|
174
|
+
"client",
|
|
175
|
+
"installToken",
|
|
176
|
+
"kind",
|
|
177
|
+
"stage",
|
|
178
|
+
"trigger",
|
|
179
|
+
"tools",
|
|
180
|
+
"mcpTool",
|
|
181
|
+
"datatypes",
|
|
182
|
+
"signature",
|
|
183
|
+
"galaxy",
|
|
184
|
+
"description",
|
|
185
|
+
]);
|
|
186
|
+
const CLIENT_KEYS = new Set(["app", "version", "platform", "wsl"]);
|
|
187
|
+
const GALAXY_KEYS = new Set(["version", "server"]);
|
|
188
|
+
const TOOL_KEYS = new Set(["id", "version"]);
|
|
189
|
+
|
|
190
|
+
const UUID_V4_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
191
|
+
const ISO_TS_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?Z$/;
|
|
192
|
+
const INSTALL_TOKEN_RE = /^[0-9a-f]{32}$/;
|
|
193
|
+
|
|
194
|
+
function isPlainObject(v) {
|
|
195
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// An unknown key is named in the error only when it looks like an identifier.
|
|
199
|
+
// Anything else is reported as <unknown>, the same string the intake Worker
|
|
200
|
+
// uses, so the error lists stay diffable and a key can't smuggle a value into
|
|
201
|
+
// an error message.
|
|
202
|
+
const REPORTABLE_KEY_RE = /^[A-Za-z_][A-Za-z0-9_]{0,39}$/;
|
|
203
|
+
|
|
204
|
+
function pushUnknownKeys(obj, allowed, prefix, errors) {
|
|
205
|
+
for (const k of Object.keys(obj)) {
|
|
206
|
+
if (allowed.has(k)) continue;
|
|
207
|
+
const name = REPORTABLE_KEY_RE.test(k) ? k : "<unknown>";
|
|
208
|
+
errors.push(`${prefix ? prefix + "." : ""}${name}:unknown-key`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function badString(v, max, { allowEmpty = false } = {}) {
|
|
213
|
+
if (typeof v !== "string") return true;
|
|
214
|
+
if (!allowEmpty && v.length === 0) return true;
|
|
215
|
+
return v.length > max;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const NON_ASCII_RE = /[^\x20-\x7E]/;
|
|
219
|
+
|
|
220
|
+
/** Every string in `value`, at any depth, that matches `re`, as `path:name`. */
|
|
221
|
+
function scanStrings(value, re, name, path = "", hits = []) {
|
|
222
|
+
if (typeof value === "string") {
|
|
223
|
+
if (path !== "installToken" && re.test(value)) hits.push(`${path}:${name}`);
|
|
224
|
+
return hits;
|
|
225
|
+
}
|
|
226
|
+
if (Array.isArray(value)) {
|
|
227
|
+
value.forEach((v, i) => scanStrings(v, re, name, `${path}[${i}]`, hits));
|
|
228
|
+
return hits;
|
|
229
|
+
}
|
|
230
|
+
if (isPlainObject(value)) {
|
|
231
|
+
for (const [k, v] of Object.entries(value)) {
|
|
232
|
+
scanStrings(v, re, name, path ? `${path}.${k}` : k, hits);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
return hits;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
export function validateObservation(obj) {
|
|
239
|
+
if (!isPlainObject(obj)) return { ok: false, errors: ["observation:not-an-object"] };
|
|
240
|
+
const o = obj;
|
|
241
|
+
const errors = [];
|
|
242
|
+
|
|
243
|
+
pushUnknownKeys(o, TOP_KEYS, "", errors);
|
|
244
|
+
if (o.schemaVersion !== OBSERVATION_SCHEMA_VERSION) errors.push("schemaVersion:unsupported");
|
|
245
|
+
if (typeof o.id !== "string" || !UUID_V4_RE.test(o.id)) errors.push("id:not-a-uuid-v4");
|
|
246
|
+
if (typeof o.clientTs !== "string" || !ISO_TS_RE.test(o.clientTs)) {
|
|
247
|
+
errors.push("clientTs:not-iso-8601");
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
if (!isPlainObject(o.client)) {
|
|
251
|
+
errors.push("client:not-an-object");
|
|
252
|
+
} else {
|
|
253
|
+
const c = o.client;
|
|
254
|
+
pushUnknownKeys(c, CLIENT_KEYS, "client", errors);
|
|
255
|
+
if (!OBSERVATION_APPS.includes(c.app)) errors.push("client.app:not-allowed");
|
|
256
|
+
if (badString(c.version, VERSION_MAX)) errors.push("client.version:bad-length");
|
|
257
|
+
if (!OBSERVATION_PLATFORMS.includes(c.platform)) errors.push("client.platform:not-allowed");
|
|
258
|
+
if (c.wsl !== undefined && typeof c.wsl !== "boolean") errors.push("client.wsl:not-a-boolean");
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
if (typeof o.installToken !== "string" || !INSTALL_TOKEN_RE.test(o.installToken)) {
|
|
262
|
+
errors.push("installToken:not-32-hex");
|
|
263
|
+
}
|
|
264
|
+
if (!OBSERVATION_KINDS.includes(o.kind)) errors.push("kind:not-allowed");
|
|
265
|
+
if (!OBSERVATION_STAGES.includes(o.stage)) errors.push("stage:not-allowed");
|
|
266
|
+
if (!OBSERVATION_TRIGGERS.includes(o.trigger)) errors.push("trigger:not-allowed");
|
|
267
|
+
|
|
268
|
+
if (!Array.isArray(o.tools)) {
|
|
269
|
+
errors.push("tools:not-an-array");
|
|
270
|
+
} else if (o.tools.length > TOOLS_MAX) {
|
|
271
|
+
errors.push("tools:too-many");
|
|
272
|
+
} else {
|
|
273
|
+
o.tools.forEach((t, i) => {
|
|
274
|
+
if (!isPlainObject(t)) {
|
|
275
|
+
errors.push(`tools[${i}]:not-an-object`);
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
pushUnknownKeys(t, TOOL_KEYS, `tools[${i}]`, errors);
|
|
279
|
+
if (badString(t.id, TOOL_ID_MAX)) errors.push(`tools[${i}].id:bad-length`);
|
|
280
|
+
if (t.version !== undefined && badString(t.version, TOOL_VERSION_MAX)) {
|
|
281
|
+
errors.push(`tools[${i}].version:bad-length`);
|
|
282
|
+
}
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (o.mcpTool !== undefined && badString(o.mcpTool, MCP_TOOL_MAX)) {
|
|
287
|
+
errors.push("mcpTool:bad-length");
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
if (!Array.isArray(o.datatypes)) {
|
|
291
|
+
errors.push("datatypes:not-an-array");
|
|
292
|
+
} else if (o.datatypes.length > DATATYPES_MAX) {
|
|
293
|
+
errors.push("datatypes:too-many");
|
|
294
|
+
} else {
|
|
295
|
+
o.datatypes.forEach((d, i) => {
|
|
296
|
+
if (badString(d, DATATYPE_MAX)) errors.push(`datatypes[${i}]:bad-length`);
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (badString(o.signature, SIGNATURE_MAX)) errors.push("signature:bad-length");
|
|
301
|
+
if (badString(o.description, DESCRIPTION_MAX, { allowEmpty: true })) {
|
|
302
|
+
errors.push("description:bad-length");
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (!isPlainObject(o.galaxy)) {
|
|
306
|
+
errors.push("galaxy:not-an-object");
|
|
307
|
+
} else {
|
|
308
|
+
const g = o.galaxy;
|
|
309
|
+
pushUnknownKeys(g, GALAXY_KEYS, "galaxy", errors);
|
|
310
|
+
const serverOk =
|
|
311
|
+
typeof g.server === "string" &&
|
|
312
|
+
(PUBLIC_GALAXY_SERVERS.includes(g.server) || g.server === PRIVATE_SERVER);
|
|
313
|
+
if (!serverOk) errors.push("galaxy.server:not-allowed");
|
|
314
|
+
if (g.version !== undefined && badString(g.version, VERSION_MAX)) {
|
|
315
|
+
errors.push("galaxy.version:bad-length");
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
// The two free-text fields carry the leak risk, so they get the full table.
|
|
320
|
+
for (const field of ["signature", "description"]) {
|
|
321
|
+
const value = o[field];
|
|
322
|
+
if (typeof value !== "string") continue;
|
|
323
|
+
for (const [name, re] of LEAK_PATTERNS) {
|
|
324
|
+
if (re.test(value)) errors.push(`${field}:${name}`);
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// EVERY string is printable ASCII, not just the free-text pair. Tool ids and
|
|
329
|
+
// datatypes arrive from model-authored arguments, so the structured half
|
|
330
|
+
// needs the same floor. Only the ASCII rule is applied here -- the rest of
|
|
331
|
+
// the table would be wrong for a field like a toolshed path. installToken is
|
|
332
|
+
// skipped for the same reason as in scanObservationForLeaks.
|
|
333
|
+
for (const hit of scanStrings(o, NON_ASCII_RE, "non-ascii")) {
|
|
334
|
+
if (!errors.includes(hit)) errors.push(hit);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return errors.length === 0 ? { ok: true } : { ok: false, errors };
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// Client-side only, on top of LEAK_PATTERNS, and deliberately NOT part of the
|
|
341
|
+
// wire validator the intake Worker mirrors: these are the shapes the contract
|
|
342
|
+
// table lets through that still name a machine, a person or a record. They run
|
|
343
|
+
// over every string in the payload (scanObservationForLeaks) and, staged, over
|
|
344
|
+
// the error line the signature came from (signatureStageLeaks). Structured fields are also admitted from
|
|
345
|
+
// allowlists in the builder, so for them this is the second line, not the
|
|
346
|
+
// first.
|
|
347
|
+
//
|
|
348
|
+
// Real top-level domains plus the usual private suffixes. A dotted run counts
|
|
349
|
+
// as a host only when its LAST label is one of these: that is what keeps
|
|
350
|
+
// `sample.fastq.gz`, `galaxy.tools.parameters.basic.ParameterValueError` and
|
|
351
|
+
// version numbers in, and it is also the rule's blind spot -- a host under a
|
|
352
|
+
// suffix not listed here (`galaxy.inrae`) gets through.
|
|
353
|
+
const HOST_SUFFIXES = [
|
|
354
|
+
// generic
|
|
355
|
+
"com|org|net|edu|gov|mil|int|info|biz|name|pro|io|ai|co|app|dev|cloud|bio|science|tech",
|
|
356
|
+
"online|site|xyz|me|tv|cc|ws|eus|cat|asia|museum",
|
|
357
|
+
// country codes in common research use
|
|
358
|
+
"us|uk|de|fr|eu|au|nz|ca|ch|at|be|nl|lu|se|no|dk|fi|is|ie|cz|sk|pl|hu|si|hr|rs|ro|bg|gr|pt|es|it",
|
|
359
|
+
"ee|lv|lt|ua|ru|tr|il|za|eg|ng|ke|in|cn|jp|kr|tw|hk|sg|my|th|vn|id|ph|br|ar|cl|mx|co|pe|uy",
|
|
360
|
+
// private and reserved
|
|
361
|
+
"internal|local|lan|corp|intranet|private|home|test|example|invalid|localhost|localdomain",
|
|
362
|
+
].join("|");
|
|
363
|
+
export const CLIENT_LEAK_PATTERNS = Object.freeze([
|
|
364
|
+
// Bounded by "not a host character" rather than \b, so a host glued to a
|
|
365
|
+
// word character (galaxy.cancer-center.org_backup) still counts.
|
|
366
|
+
Object.freeze([
|
|
367
|
+
"hostname",
|
|
368
|
+
new RegExp(
|
|
369
|
+
`(?<![A-Za-z0-9-])[A-Za-z0-9-]+(?:\\.[A-Za-z0-9-]+)*\\.(?:${HOST_SUFFIXES})(?![A-Za-z0-9-])`,
|
|
370
|
+
"i",
|
|
371
|
+
),
|
|
372
|
+
]),
|
|
373
|
+
// A single-label host with a port: galaxyprod:8080, galaxy_prod:8080 (a
|
|
374
|
+
// Docker service name), localhost:80. Ports 80 and 443 by name, otherwise
|
|
375
|
+
// four to six digits (six, because the early stage of the signature scan
|
|
376
|
+
// sees the port before <n> does), so `line:42` and `HTTPError:400` stay
|
|
377
|
+
// out.
|
|
378
|
+
Object.freeze(["host-port", /(?<![\w-])[A-Za-z][\w-]*:(?:80|443|\d{4,6})\b/]),
|
|
379
|
+
// Anything at anything: the contract's email rule needs a dotted domain, and
|
|
380
|
+
// alice@localhost, alice@7node and alice@3lab don't have one.
|
|
381
|
+
Object.freeze(["user-at-host", /[^\s@]@[A-Za-z0-9]/]),
|
|
382
|
+
// No word boundaries: node_10.12.4.7, srv_192.168.17.42 and 10.0.0.5x are
|
|
383
|
+
// still addresses.
|
|
384
|
+
Object.freeze(["ipv4", /\d{1,3}(?:\.\d{1,3}){3}/]),
|
|
385
|
+
Object.freeze([
|
|
386
|
+
"ipv6",
|
|
387
|
+
/\[[0-9A-Fa-f:.]*:[0-9A-Fa-f:.]*\]|[0-9A-Fa-f]{0,4}::[0-9A-Fa-f]{0,4}|(?:[0-9A-Fa-f]{1,4}:){3,}[0-9A-Fa-f]{1,4}/,
|
|
388
|
+
]),
|
|
389
|
+
Object.freeze(["uuid", /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/i]),
|
|
390
|
+
// Any scheme, not just http: s3://, gs://, ftp://, file://.
|
|
391
|
+
Object.freeze(["scheme-url", /[A-Za-z][A-Za-z0-9+.-]*:\/\//]),
|
|
392
|
+
// Any path separator at all. The normalizer turns a rooted path into
|
|
393
|
+
// <path>, which has none, so what is left is a relative path, a one-segment
|
|
394
|
+
// path (/Alice_Smith), the tail of a path with a space in it, or a UNC
|
|
395
|
+
// share -- and from here none of them can be told apart from a name. The
|
|
396
|
+
// cost, in the fail-closed direction: "and/or" and "400/500" refuse too.
|
|
397
|
+
Object.freeze(["path-separator", /[\w-]*[\\/]|%2[Ff]|%5[Cc]/]),
|
|
398
|
+
Object.freeze(["tilde-user", /~[A-Za-z_]/]),
|
|
399
|
+
// The contract's id phrase misses `history_id=12`, `dataset id: 42`,
|
|
400
|
+
// `histories 12 and 13` and other plurals. A bare job or invocation number
|
|
401
|
+
// is left alone -- Galaxy's own messages say "Job 3 is in error state", and
|
|
402
|
+
// a small decoded job number names nothing -- but one labelled as an id
|
|
403
|
+
// (`invocation_id=73`, `job id 9`) is an id. "history number 12" and
|
|
404
|
+
// "dataset no. 42" are the same phrase with a word in the way.
|
|
405
|
+
Object.freeze([
|
|
406
|
+
"id-phrase",
|
|
407
|
+
/\b(?:histor(?:y|ies)|datasets?|hids?|collections?|hdas?|hdcas?)(?:[\s_-]?ids?)?\b[^A-Za-z0-9]{0,4}(?:(?:number|num|no)\b[^A-Za-z0-9]{0,4})?\d|\b(?:jobs?|invocations?|workflows?|users?)[\s_-]?ids?\b[^A-Za-z0-9]{0,4}\d/i,
|
|
408
|
+
]),
|
|
409
|
+
]);
|
|
410
|
+
|
|
411
|
+
/** Names of every leak pattern (contract and client-side) that `text` trips. */
|
|
412
|
+
export function textLeaks(text) {
|
|
413
|
+
const hits = [];
|
|
414
|
+
if (typeof text !== "string") return hits;
|
|
415
|
+
for (const [name, re] of LEAK_PATTERNS) if (re.test(text)) hits.push(name);
|
|
416
|
+
for (const [name, re] of CLIENT_LEAK_PATTERNS) if (re.test(text)) hits.push(name);
|
|
417
|
+
return hits;
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// A public toolshed id is a host and a path by construction, so scanning it
|
|
421
|
+
// whole would always trip. Its host is fixed, and the segments after it are
|
|
422
|
+
// what the model wrote, so those are scanned one at a time instead.
|
|
423
|
+
const PUBLIC_TOOLSHED_PREFIX_RE =
|
|
424
|
+
/^(?:toolshed\.g2\.bx\.psu\.edu|testtoolshed\.g2\.bx\.psu\.edu)\/repos\//;
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* The strings in a payload that the leak table runs over, as [path, text]. A
|
|
428
|
+
* few fields are exact-shape by contract and would trip a rule by design (a
|
|
429
|
+
* UUID id, an ISO timestamp, an allowlisted server name, the 32-hex install
|
|
430
|
+
* token); each is skipped only while it actually has that exact shape, so a
|
|
431
|
+
* hostile value in one of them is still scanned.
|
|
432
|
+
*/
|
|
433
|
+
function scannableStrings(obs) {
|
|
434
|
+
const out = [];
|
|
435
|
+
const walk = (value, path) => {
|
|
436
|
+
if (typeof value === "string") {
|
|
437
|
+
if (path === "installToken" && INSTALL_TOKEN_RE.test(value)) return;
|
|
438
|
+
if (path === "id" && UUID_V4_RE.test(value)) return;
|
|
439
|
+
if (path === "clientTs" && ISO_TS_RE.test(value)) return;
|
|
440
|
+
if (path === "galaxy.server" && PUBLIC_GALAXY_SERVERS.includes(value)) return;
|
|
441
|
+
if (/^tools\[\d+\]\.id$/.test(path) && PUBLIC_TOOLSHED_PREFIX_RE.test(value)) {
|
|
442
|
+
value
|
|
443
|
+
.replace(PUBLIC_TOOLSHED_PREFIX_RE, "")
|
|
444
|
+
.split("/")
|
|
445
|
+
.forEach((seg, i) => out.push([`${path}/${i}`, seg]));
|
|
446
|
+
return;
|
|
447
|
+
}
|
|
448
|
+
out.push([path, value]);
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
if (Array.isArray(value)) {
|
|
452
|
+
value.forEach((v, i) => walk(v, `${path}[${i}]`));
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
if (isPlainObject(value)) {
|
|
456
|
+
for (const [k, v] of Object.entries(value)) walk(v, path ? `${path}.${k}` : k);
|
|
457
|
+
}
|
|
458
|
+
};
|
|
459
|
+
walk(obs, "");
|
|
460
|
+
return out;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Run the whole leak table -- the contract's and the client-side one -- over
|
|
465
|
+
* EVERY string in the payload, at any depth. Structured fields are admitted
|
|
466
|
+
* from allowlists upstream, so on a builder-made payload this comes back
|
|
467
|
+
* empty; when it does not, the caller drops the observation. Hits are
|
|
468
|
+
* `path:pattern`; a hit in a toolshed id segment is reported against the id.
|
|
469
|
+
*/
|
|
470
|
+
export function scanObservationForLeaks(obs) {
|
|
471
|
+
const hits = [];
|
|
472
|
+
for (const [path, text] of scannableStrings(obs)) {
|
|
473
|
+
for (const name of textLeaks(text)) hits.push(`${path.replace(/\/\d+$/, "")}:${name}`);
|
|
474
|
+
}
|
|
475
|
+
return [...new Set(hits)];
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
function sliceOr(v, max, fallback) {
|
|
479
|
+
return typeof v === "string" ? v.slice(0, max) : fallback;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Coerce a built observation into the contract's shape and caps. Pair with
|
|
484
|
+
* validateObservation: cap first ("make it fit"), then validate ("is it
|
|
485
|
+
* legal"). Truncation is a plain slice -- an ellipsis character would make the
|
|
486
|
+
* field non-ASCII and the validator would reject it.
|
|
487
|
+
*/
|
|
488
|
+
export function capObservation(obs) {
|
|
489
|
+
const o = isPlainObject(obs) ? obs : {};
|
|
490
|
+
const client = isPlainObject(o.client) ? o.client : {};
|
|
491
|
+
const galaxy = isPlainObject(o.galaxy) ? o.galaxy : {};
|
|
492
|
+
const tools = (Array.isArray(o.tools) ? o.tools : [])
|
|
493
|
+
.slice(0, TOOLS_MAX)
|
|
494
|
+
.map((t) => {
|
|
495
|
+
const src = isPlainObject(t) ? t : {};
|
|
496
|
+
const version = sliceOr(src.version, TOOL_VERSION_MAX, "");
|
|
497
|
+
return {
|
|
498
|
+
id: sliceOr(src.id, TOOL_ID_MAX, ""),
|
|
499
|
+
...(version ? { version } : {}),
|
|
500
|
+
};
|
|
501
|
+
})
|
|
502
|
+
.filter((t) => t.id.length > 0);
|
|
503
|
+
const datatypes = (Array.isArray(o.datatypes) ? o.datatypes : [])
|
|
504
|
+
.slice(0, DATATYPES_MAX)
|
|
505
|
+
.map((d) => sliceOr(d, DATATYPE_MAX, ""))
|
|
506
|
+
.filter((d) => d.length > 0);
|
|
507
|
+
const mcpTool = sliceOr(o.mcpTool, MCP_TOOL_MAX, "");
|
|
508
|
+
const galaxyVersion = sliceOr(galaxy.version, VERSION_MAX, "");
|
|
509
|
+
|
|
510
|
+
return {
|
|
511
|
+
schemaVersion: OBSERVATION_SCHEMA_VERSION,
|
|
512
|
+
id: o.id,
|
|
513
|
+
clientTs: o.clientTs,
|
|
514
|
+
client: {
|
|
515
|
+
app: client.app,
|
|
516
|
+
version: sliceOr(client.version, VERSION_MAX, ""),
|
|
517
|
+
platform: client.platform,
|
|
518
|
+
...(client.wsl === true ? { wsl: true } : {}),
|
|
519
|
+
},
|
|
520
|
+
installToken: o.installToken,
|
|
521
|
+
kind: o.kind,
|
|
522
|
+
stage: o.stage,
|
|
523
|
+
trigger: o.trigger,
|
|
524
|
+
tools,
|
|
525
|
+
...(mcpTool ? { mcpTool } : {}),
|
|
526
|
+
datatypes,
|
|
527
|
+
signature: sliceOr(o.signature, SIGNATURE_MAX, ""),
|
|
528
|
+
galaxy: {
|
|
529
|
+
server: galaxy.server,
|
|
530
|
+
...(galaxyVersion ? { version: galaxyVersion } : {}),
|
|
531
|
+
},
|
|
532
|
+
description: sliceOr(o.description, DESCRIPTION_MAX, ""),
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const textEncoder = new TextEncoder();
|
|
537
|
+
|
|
538
|
+
export function observationByteLength(obs) {
|
|
539
|
+
try {
|
|
540
|
+
return textEncoder.encode(JSON.stringify(obs)).length;
|
|
541
|
+
} catch {
|
|
542
|
+
return Number.POSITIVE_INFINITY;
|
|
543
|
+
}
|
|
544
|
+
}
|