hecks 3.9.1 → 3.11.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.
- checksums.yaml +4 -4
- data/examples/pizzas/bluebook/pizzas.bluebook +29 -0
- data/examples/pizzas/bluebook/pizzas.hecksagon +7 -0
- data/exe/hecks +4 -4
- data/lib/hecks/adapters/driven/d1.rb +2 -10
- data/lib/hecks/adapters/driven/heki.adapter +1 -1
- data/lib/hecks/adapters/driven/heki.rb +2 -12
- data/lib/hecks/adapters/driven/lambda.adapter +1 -1
- data/lib/hecks/adapters/driven/lambda.rb +3 -18
- data/lib/hecks/adapters/driven/postgres.rb +4 -11
- data/lib/hecks/adapters/driven/sqlite.adapter +1 -1
- data/lib/hecks/adapters/driven/sqlite.rb +2 -10
- data/lib/hecks/adapters/driving/admission.rb +46 -0
- data/lib/hecks/adapters/driving/cli/nesting.rb +36 -0
- data/lib/hecks/adapters/driving/cli/shorthand.rb +88 -0
- data/lib/hecks/adapters/driving/cli.rb +127 -0
- data/lib/hecks/adapters/driving/cli_runner/answers.rb +160 -0
- data/lib/hecks/adapters/driving/cli_runner/resolution.rb +155 -0
- data/lib/hecks/adapters/driving/cli_runner/settling.rb +120 -0
- data/lib/hecks/adapters/driving/cli_runner/streaming.rb +93 -0
- data/lib/hecks/adapters/driving/cli_runner/suggestions.rb +47 -0
- data/lib/hecks/adapters/driving/cli_runner.rb +69 -0
- data/lib/hecks/adapters/driving/command_request.rb +121 -0
- data/lib/hecks/adapters/driving/handle.rb +214 -0
- data/lib/hecks/adapters/driving/json.rb +136 -0
- data/lib/hecks/adapters/driving/launcher_options.rb +267 -0
- data/lib/hecks/adapters/driving/mcp/properties.rb +49 -0
- data/lib/hecks/adapters/driving/mcp/tool_calls.rb +116 -0
- data/lib/hecks/adapters/driving/mcp/tool_definitions/auditing.rb +53 -0
- data/lib/hecks/adapters/driving/mcp/tool_definitions/calling.rb +71 -0
- data/lib/hecks/adapters/driving/mcp/tool_definitions/reading.rb +99 -0
- data/lib/hecks/adapters/driving/mcp/tool_definitions.rb +18 -0
- data/lib/hecks/adapters/driving/mcp.rb +159 -0
- data/lib/hecks/adapters/driving/mcp_guide_cache.rb +78 -0
- data/lib/hecks/adapters/driving/mcp_scope/argument_policy.rb +97 -0
- data/lib/hecks/adapters/driving/mcp_scope/env_problems.rb +69 -0
- data/lib/hecks/adapters/driving/mcp_scope/presenter.rb +83 -0
- data/lib/hecks/adapters/driving/mcp_scope.rb +213 -0
- data/lib/hecks/adapters/driving/ruby/aggregate_module.rb +171 -0
- data/lib/hecks/adapters/driving/ruby/chapter.rb +91 -0
- data/lib/hecks/adapters/driving/ruby.rb +83 -0
- data/lib/hecks/adapters/driving/usage_cache.rb +144 -0
- data/lib/hecks/{doors.rb → adapters/driving.rb} +10 -6
- data/lib/hecks/behaviors/expectations/boot.rb +1 -1
- data/lib/hecks/bench/ruby_runner.rb +1 -1
- data/lib/hecks/bluebook/ask_resolution.rb +130 -0
- data/lib/hecks/bluebook/assembly/aggregate_assembly.rb +8 -2
- data/lib/hecks/bluebook/assembly/contracts/rules.rb +4 -2
- data/lib/hecks/bluebook/assembly/contracts/structure.rb +3 -1
- data/lib/hecks/bluebook/assembly/marks.rb +1 -0
- data/lib/hecks/bluebook/attribute.rb +1 -1
- data/lib/hecks/bluebook/behaviour/command.rb +13 -8
- data/lib/hecks/bluebook/behaviour/policy.rb +30 -0
- data/lib/hecks/bluebook/capabilities/attribute_values.rb +72 -0
- data/lib/hecks/bluebook/capabilities.rb +91 -9
- data/lib/hecks/bluebook/domain_port.rb +16 -4
- data/lib/hecks/bluebook/dsl/adapter_builder.rb +18 -6
- data/lib/hecks/bluebook/dsl/binding_proxy.rb +13 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/computed_sources.rb +98 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/fan_out_rows.rb +42 -0
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/projected_fields.rb +14 -2
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/provisions.rb +69 -3
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation/with_projections.rb +17 -10
- data/lib/hecks/bluebook/dsl/bluebook_builder/validation.rb +5 -0
- data/lib/hecks/bluebook/dsl/bootstrap_table.rb +2 -0
- data/lib/hecks/bluebook/dsl/command_builder/mutations.rb +8 -1
- data/lib/hecks/bluebook/dsl/command_builder/operands.rb +121 -0
- data/lib/hecks/bluebook/dsl/command_builder.rb +2 -0
- data/lib/hecks/bluebook/dsl/hecksagon_builder.rb +10 -1
- data/lib/hecks/bluebook/dsl/lifecycle_builder.rb +45 -2
- data/lib/hecks/bluebook/dsl/policy_builder.rb +47 -10
- data/lib/hecks/bluebook/dsl/rule_reference.rb +7 -4
- data/lib/hecks/bluebook/dsl/world_builder.rb +1 -1
- data/lib/hecks/bluebook/expression/ast_json/emitters.rb +6 -0
- data/lib/hecks/bluebook/expression/ast_json.rb +9 -6
- data/lib/hecks/bluebook/expression/ast_reader.rb +14 -9
- data/lib/hecks/bluebook/expression/projection.json +36 -0
- data/lib/hecks/bluebook/expression/resolver/operations.rb +31 -0
- data/lib/hecks/bluebook/expression/resolver/parsing.rb +38 -5
- data/lib/hecks/bluebook/expression/resolver/scanning.rb +22 -5
- data/lib/hecks/bluebook/expression/resolver/values.rb +10 -0
- data/lib/hecks/bluebook/expression/resolver.rb +20 -1
- data/lib/hecks/bluebook/hecksagon.rb +23 -3
- data/lib/hecks/bluebook/lifecycle.rb +22 -3
- data/lib/hecks/bluebook/meta_validator/adapter_judge.rb +13 -1
- data/lib/hecks/bluebook/meta_validator/modes.rb +4 -1
- data/lib/hecks/bluebook/meta_validator/readings.rb +18 -1
- data/lib/hecks/bluebook/meta_validator/reconstruction/heads.rb +2 -1
- data/lib/hecks/bluebook/meta_validator/shapes.rb +5 -0
- data/lib/hecks/bluebook/meta_validator/verdict_cache.rb +1 -1
- data/lib/hecks/bluebook/meta_validator.rb +12 -12
- data/lib/hecks/bluebook/model_check/ask_checks.rb +71 -0
- data/lib/hecks/bluebook/model_check/policy_checks.rb +2 -1
- data/lib/hecks/bluebook/model_check/reference_checks.rb +70 -0
- data/lib/hecks/bluebook/model_check.rb +8 -2
- data/lib/hecks/bluebook/policy.rb +4 -2
- data/lib/hecks/bluebook/smoke_test.rb +1 -1
- data/lib/hecks/bluebook/synthesizer.rb +2 -0
- data/lib/hecks/bluebook.rb +1 -0
- data/lib/hecks/cli/command_table.rb +1 -1
- data/lib/hecks/cli/console.rb +2 -1
- data/lib/hecks/cli/document.rb +1 -1
- data/lib/hecks/cli/launcher_words.rb +4 -4
- data/lib/hecks/cli/mcp.rb +4 -4
- data/lib/hecks/cli/project_cli/launcher_source.rb +2 -2
- data/lib/hecks/cli/project_cli/templates.rb +5 -5
- data/lib/hecks/cli/project_cli.rb +9 -6
- data/lib/hecks/cli/run.rb +2 -2
- data/lib/hecks/cli/smoke_http/checks.rb +4 -1
- data/lib/hecks/cli/smoke_http/transport.rb +3 -3
- data/lib/hecks/cli/smoke_http.rb +3 -0
- data/lib/hecks/codemod.rb +5 -2
- data/lib/hecks/corpus/routes.rb +7 -0
- data/lib/hecks/corpus/rust.rb +3 -2
- data/lib/hecks/deploy/bluebook/deploy.bluebook +124 -2
- data/lib/hecks/deploy/bluebook/deploy.hecksagon +8 -0
- data/lib/hecks/deploy/oidc.json +16 -3
- data/lib/hecks/doc/reference/rendering.rb +6 -3
- data/lib/hecks/forms/app/commands.rb +8 -7
- data/lib/hecks/forms/app/queries.rb +2 -1
- data/lib/hecks/forms/app.rb +1 -1
- data/lib/hecks/forms/record_renderer.rb +1 -1
- data/lib/hecks/framework/bluebook/privacy.bluebook +11 -0
- data/lib/hecks/fuzzing/concurrent_dispatch.rb +4 -1
- data/lib/hecks/fuzzing/failure_triage.rb +2 -1
- data/lib/hecks/fuzzing/mutation/run.rb +11 -4
- data/lib/hecks/fuzzing/properties/catalog.rb +6 -4
- data/lib/hecks/fuzzing/properties/guaranteed_by_construction.rb +18 -11
- data/lib/hecks/fuzzing/properties/guards.rb +1 -1
- data/lib/hecks/fuzzing/self_consistency/saga_redelivery.rb +1 -1
- data/lib/hecks/fuzzing/self_consistency.rb +6 -2
- data/lib/hecks/fuzzing/shrinker.rb +3 -1
- data/lib/hecks/fuzzing/value_generator.rb +19 -5
- data/lib/hecks/gate/stages.yml +4 -10
- data/lib/hecks/grammar/evolve.rb +1 -1
- data/lib/hecks/grammar/expression_operators.json +372 -0
- data/lib/hecks/grammar/operators.rb +3 -1
- data/lib/hecks/hecks/adapters/codebase/corpus_tasks.rb +10 -10
- data/lib/hecks/hecks/adapters/codebase/inspection/rubycritic.rb +35 -0
- data/lib/hecks/hecks/adapters/codebase/inspection.rb +104 -0
- data/lib/hecks/hecks/adapters/codebase/publishing.rb +2 -1
- data/lib/hecks/hecks/adapters/codebase/release_facts.rb +12 -2
- data/lib/hecks/hecks/adapters/codebase/source_tree.rb +2 -1
- data/lib/hecks/hecks/adapters/codebase/test_runner.rb +1 -1
- data/lib/hecks/hecks/adapters/cost_explorer.rb +8 -5
- data/lib/hecks/hecks/adapters/deploy_toolchain/clearing.rb +51 -0
- data/lib/hecks/hecks/adapters/deploy_toolchain/statuses.rb +2 -1
- data/lib/hecks/hecks/adapters/deploy_toolchain.rb +2 -0
- data/lib/hecks/hecks/adapters/finding_github.rb +13 -6
- data/lib/hecks/hecks/adapters/first_admin.rb +2 -2
- data/lib/hecks/hecks/adapters/github_rulesets.rb +6 -3
- data/lib/hecks/hecks/adapters/in_process_boot.rb +1 -1
- data/lib/hecks/hecks/adapters/journal_store/compaction.rb +1 -1
- data/lib/hecks/hecks/adapters/journal_store/edge_audit.rb +10 -20
- data/lib/hecks/hecks/adapters/journal_store/held_domain.rb +28 -5
- data/lib/hecks/hecks/adapters/local_files.rb +2 -2
- data/lib/hecks/hecks/adapters/rehearsal/audit_report.rb +107 -0
- data/lib/hecks/hecks/adapters/rehearsal/census.rb +71 -0
- data/lib/hecks/hecks/adapters/rehearsal/declaration.rb +133 -0
- data/lib/hecks/hecks/adapters/rehearsal/environment.rb +83 -0
- data/lib/hecks/hecks/adapters/rehearsal/inquiry.rb +142 -0
- data/lib/hecks/hecks/adapters/rehearsal/memory_store.rb +96 -0
- data/lib/hecks/hecks/adapters/rehearsal/postgres_census.rb +100 -0
- data/lib/hecks/hecks/adapters/rehearsal/postgres_reading.rb +69 -0
- data/lib/hecks/hecks/adapters/rehearsal/postgres_store.rb +129 -0
- data/lib/hecks/hecks/adapters/rehearsal/rows.rb +41 -0
- data/lib/hecks/hecks/adapters/rehearsal/rust_audit.rb +83 -0
- data/lib/hecks/hecks/adapters/rehearsal/subject.rb +104 -0
- data/lib/hecks/hecks/adapters/rehearsal.rb +54 -0
- data/lib/hecks/hecks/adapters/rehearsal_store.adapter +7 -0
- data/lib/hecks/hecks/adapters/rehearsal_store.rb +81 -0
- data/lib/hecks/hecks/adapters/site_toolchain/role_prober.rb +78 -0
- data/lib/hecks/hecks/adapters/site_toolchain.rb +29 -4
- data/lib/hecks/hecks/adapters/terminal.rb +13 -11
- data/lib/hecks/hecks/adapters/world_clearing.rb +65 -0
- data/lib/hecks/hecks/codebase.behaviors +13 -1
- data/lib/hecks/hecks/codebase.bluebook +239 -5
- data/lib/hecks/hecks/custodian.behaviors +24 -6
- data/lib/hecks/hecks/custodian.bluebook +6 -1227
- data/lib/hecks/hecks/deploy.behaviors +1 -1
- data/lib/hecks/hecks/deploy.world +1 -3
- data/lib/hecks/hecks/hecks.behaviors +1 -1
- data/lib/hecks/hecks/hecks.hecksagon +61 -18
- data/lib/hecks/hecks/hecks.world +11 -10
- data/lib/hecks/hecks/rehearsal.bluebook +477 -0
- data/lib/hecks/hecks/site.behaviors +1 -1
- data/lib/hecks/hecks/site.world +0 -1
- data/lib/hecks/hecks/tooling.bluebook +1238 -0
- data/lib/hecks/indifferent_key.rb +20 -0
- data/lib/hecks/language/adapter.bluebook +13 -3
- data/lib/hecks/language/bluebook/aggregate.bluebook +23 -0
- data/lib/hecks/language/bluebook/bluebook.bluebook +15 -0
- data/lib/hecks/language/bluebook/entity.bluebook +18 -0
- data/lib/hecks/language/bluebook/policy.bluebook +9 -1
- data/lib/hecks/language/bluebook/query.bluebook +1 -1
- data/lib/hecks/language/bluebook/vocabulary.bluebook +11 -4
- data/lib/hecks/language/hecksagon/domain_port.bluebook +1 -1
- data/lib/hecks/language/hecksagon/hecksagon.bluebook +22 -0
- data/lib/hecks/language/oidc.json +10 -0
- data/lib/hecks/language/port.bluebook +4 -4
- data/lib/hecks/literal.rb +12 -3
- data/lib/hecks/ports/key_vault.rb +7 -2
- data/lib/hecks/ports/persistence/plugins/era/era_check.rb +21 -10
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/boot.rb +4 -2
- data/lib/hecks/ports/persistence/plugins/era/postgres_era/lineage_manager/era_resolver.rb +2 -1
- data/lib/hecks/ports/persistence/repository_factory.rb +6 -3
- data/lib/hecks/projections/deploy/box/templates/deploy-box.sh.tmpl +29 -0
- data/lib/hecks/projections/deploy/box/templates/deploy-service.sh.tmpl +1 -1
- data/lib/hecks/projections/deploy/fargate/settings/tuning.rb +15 -0
- data/lib/hecks/projections/deploy/fargate/settings.rb +3 -1
- data/lib/hecks/projections/deploy/fargate/stack/readers.rb +1 -0
- data/lib/hecks/projections/deploy/preview/preview.sh.erb +1 -1
- data/lib/hecks/projections/deploy/shared.rb +5 -2
- data/lib/hecks/projections/deploy/templates/fargate/Makefile.tmpl +7 -4
- data/lib/hecks/projections/model/deviations.rb +2 -2
- data/lib/hecks/projections/model.rb +2 -2
- data/lib/hecks/projections/oidc.rb +13 -1
- data/lib/hecks/projections/shape.rb +5 -2
- data/lib/hecks/projections/site/cms_editor/attributes.rb +95 -0
- data/lib/hecks/projections/site/cms_editor/blocks.rb +157 -0
- data/lib/hecks/projections/site/cms_editor/checks.rb +77 -0
- data/lib/hecks/projections/site/cms_editor/clearing.rb +70 -0
- data/lib/hecks/projections/site/cms_editor/color.rb +124 -0
- data/lib/hecks/projections/site/cms_editor/composition.rb +42 -0
- data/lib/hecks/projections/site/cms_editor/destructive.rb +61 -0
- data/lib/hecks/projections/site/cms_editor/drafts.rb +102 -0
- data/lib/hecks/projections/site/cms_editor/hints.rb +74 -0
- data/lib/hecks/projections/site/cms_editor/icons.rb +86 -0
- data/lib/hecks/projections/site/cms_editor/key_kinds.rb +31 -0
- data/lib/hecks/projections/site/cms_editor/limits.rb +72 -0
- data/lib/hecks/projections/site/cms_editor/media.rb +85 -0
- data/lib/hecks/projections/site/cms_editor/moment.rb +34 -0
- data/lib/hecks/projections/site/cms_editor/palette.rb +115 -0
- data/lib/hecks/projections/site/cms_editor/pickers.rb +85 -0
- data/lib/hecks/projections/site/cms_editor/pretty.rb +40 -0
- data/lib/hecks/projections/site/cms_editor/preview.rb +91 -0
- data/lib/hecks/projections/site/cms_editor/relations.rb +92 -0
- data/lib/hecks/projections/site/cms_editor/scale_checks.rb +66 -0
- data/lib/hecks/projections/site/cms_editor/scheduling.rb +150 -0
- data/lib/hecks/projections/site/cms_editor/schema.rb +161 -0
- data/lib/hecks/projections/site/cms_editor/setting.rb +130 -0
- data/lib/hecks/projections/site/cms_editor/targets.rb +86 -0
- data/lib/hecks/projections/site/cms_editor/templates/package.json.tmpl +36 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/app.ts.tmpl +370 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/assets.ts.tmpl +98 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/auth/membership.ts.tmpl +87 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/auth/session.ts.tmpl +36 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/auth/sso.ts.tmpl +20 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/app.css.tmpl +412 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/autosave.js.tmpl +144 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/blocks.js.tmpl +500 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/body_doc.js.tmpl +175 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/body_editor.js.tmpl +418 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/epoch.js.tmpl +61 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/forms.js.tmpl +210 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/lists.js.tmpl +40 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/main.js.tmpl +26 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/media_picker.js.tmpl +232 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/moments.js.tmpl +73 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/pickers.js.tmpl +152 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/preview.js.tmpl +98 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/shell.js.tmpl +140 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/browser/toast.js.tmpl +66 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/choices.ts.tmpl +99 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/commands.ts.tmpl +133 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/config.ts.tmpl +29 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/flash.ts.tmpl +52 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/host.ts.tmpl +43 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/media/handler.ts.tmpl +177 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/media/multipart.ts.tmpl +53 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/media/sniff.ts.tmpl +94 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/media/storage.ts.tmpl +50 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/names.ts.tmpl +36 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/schema.ts.tmpl +214 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/server.ts.tmpl +65 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/availability.ts.tmpl +15 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/block_list.js.tmpl +213 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/blocks.ts.tmpl +275 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/body_model.js.tmpl +123 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/body_parse.js.tmpl +320 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/detail.ts.tmpl +88 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/dialogs.ts.tmpl +43 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/fields.ts.tmpl +275 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/html.ts.tmpl +61 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/icons.ts.tmpl +15 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/input.ts.tmpl +92 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/links.ts.tmpl +100 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/moments.ts.tmpl +44 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/outline.ts.tmpl +63 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/pages.ts.tmpl +213 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/paging.ts.tmpl +82 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/pickers.ts.tmpl +110 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/preview.ts.tmpl +87 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/related.ts.tmpl +97 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/schedule.ts.tmpl +160 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/shell.ts.tmpl +211 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/table.ts.tmpl +151 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/theme.js.tmpl +16 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/values.ts.tmpl +69 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/words.ts.tmpl +97 -0
- data/lib/hecks/projections/site/cms_editor/templates/src/ui/world.ts.tmpl +35 -0
- data/lib/hecks/projections/site/cms_editor/templates/tsconfig.json.tmpl +17 -0
- data/lib/hecks/projections/site/cms_editor/theme.rb +29 -0
- data/lib/hecks/projections/site/cms_editor/theme_checks.rb +57 -0
- data/lib/hecks/projections/site/cms_editor.rb +133 -0
- data/lib/hecks/projections/site/role_probe.rb +74 -0
- data/lib/hecks/projector/cli_audience.rb +2 -1
- data/lib/hecks/projector/cli_projector/listing.rb +1 -1
- data/lib/hecks/projector/cli_projector/option_specs.rb +1 -1
- data/lib/hecks/projector/docs_projector/closing.rb +1 -1
- data/lib/hecks/projector/docs_projector.rb +1 -1
- data/lib/hecks/projector/exporter/bindings.rb +40 -10
- data/lib/hecks/projector/exporter/capability_values.rb +55 -0
- data/lib/hecks/projector/narrate_projector/reactions.rb +1 -1
- data/lib/hecks/projector/target.rb +11 -0
- data/lib/hecks/projector.rb +32 -5
- data/lib/hecks/quality_control/adapters/github_checks.rb +12 -8
- data/lib/hecks/quality_control/adapters/github_issues.rb +8 -4
- data/lib/hecks/quality_control/cli/qa_open_pr.rb +2 -2
- data/lib/hecks/quality_control/cli/qa_sweep/checks/shrinking.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_sweep/finding_report.rb +1 -1
- data/lib/hecks/quality_control/cli/qa_sweep.rb +1 -1
- data/lib/hecks/quality_control/quality_control.bluebook +6 -2
- data/lib/hecks/release/approval.rb +64 -0
- data/lib/hecks/router/{door.rb → aggregate_reader.rb} +5 -4
- data/lib/hecks/router/namespace_installer.rb +16 -16
- data/lib/hecks/router.rb +1 -1
- data/lib/hecks/runtime/best_effort.rb +22 -0
- data/lib/hecks/runtime/command_interpreter/mutation_applier.rb +12 -2
- data/lib/hecks/runtime/command_interpreter.rb +3 -2
- data/lib/hecks/runtime/command_rules/emission.rb +1 -1
- data/lib/hecks/runtime/dispatcher.rb +4 -4
- data/lib/hecks/runtime/entity_element/location.rb +1 -1
- data/lib/hecks/runtime/event.rb +5 -0
- data/lib/hecks/runtime/identity.rb +7 -6
- data/lib/hecks/runtime/interpreting.rb +2 -1
- data/lib/hecks/runtime/invocation_builder.rb +1 -1
- data/lib/hecks/runtime/loader/boot_steps.rb +8 -5
- data/lib/hecks/runtime/loader.rb +10 -10
- data/lib/hecks/runtime/policy_interpreter/arguments.rb +7 -4
- data/lib/hecks/runtime/policy_interpreter/fan_out.rb +3 -3
- data/lib/hecks/runtime/policy_interpreter.rb +6 -6
- data/lib/hecks/runtime/port_operation_interpreter.rb +1 -1
- data/lib/hecks/runtime/reaction_invocation.rb +1 -1
- data/lib/hecks/runtime/rebuild_sweep.rb +11 -1
- data/lib/hecks/runtime/registry/ask_verification.rb +35 -0
- data/lib/hecks/runtime/registry/hecksagon_rules.rb +10 -0
- data/lib/hecks/runtime/registry/merging.rb +1 -1
- data/lib/hecks/runtime/registry/providers.rb +16 -0
- data/lib/hecks/runtime/registry/read_repository.rb +6 -4
- data/lib/hecks/runtime/registry/verification.rb +4 -1
- data/lib/hecks/runtime/registry/world_defaults.rb +29 -0
- data/lib/hecks/runtime/registry.rb +1 -1
- data/lib/hecks/runtime/saga_interpreter/compensation.rb +1 -1
- data/lib/hecks/runtime/saga_interpreter/delivery.rb +4 -4
- data/lib/hecks/runtime/saga_interpreter/transitions.rb +1 -1
- data/lib/hecks/runtime/saga_interpreter.rb +3 -3
- data/lib/hecks/runtime/tenant_check.rb +5 -4
- data/lib/hecks/runtime/value/coercion.rb +2 -2
- data/lib/hecks/runtime/value/validation.rb +16 -4
- data/lib/hecks/runtime.rb +9 -9
- data/lib/hecks/rust_build/project_rust/target_ir.rb +1 -1
- data/lib/hecks/rust_build/wasm.rb +1 -1
- data/lib/hecks/rust_build/wasm_browser.rb +1 -1
- data/lib/hecks/rust_build.rb +11 -0
- data/lib/hecks/served_options.rb +53 -0
- data/lib/hecks/site/bluebook/site.bluebook +29 -1
- data/lib/hecks/site/bluebook/site.ports.hecksagon +6 -0
- data/lib/hecks/storehouse/audit.rb +1 -1
- data/lib/hecks/storehouse/dispatching.rb +8 -6
- data/lib/hecks/storehouse/introspection.rb +2 -2
- data/lib/hecks/storehouse/querying.rb +6 -4
- data/lib/hecks/storehouse.rb +4 -4
- data/lib/hecks/tools/comment_style/source_file.rb +4 -1
- data/lib/hecks/tools/deploy_recipe_lint/fixtures.rb +4 -1
- data/lib/hecks/tools/lanes/rows.rb +6 -0
- data/lib/hecks/tools/oidc_manifests.rb +4 -5
- data/lib/hecks/tools/regeneration_run.rb +11 -4
- data/lib/hecks/tools/site_routes/cli.rb +1 -1
- data/lib/hecks/tools/site_routes/editor_files.rb +45 -0
- data/lib/hecks/tools/site_routes.rb +14 -7
- data/lib/hecks/tools/tenant_provisioning.rb +2 -2
- data/lib/hecks/tools/tools_doc.rb +4 -4
- data/lib/hecks/version.rb +1 -1
- data/lib/hecks/vocabulary.rb +4 -4
- data/lib/hecks.rb +15 -15
- data/lib/rubocop/cop/hecks/env_dump.rb +69 -0
- data/lib/rubocop/cop/hecks/monotonic_duration.rb +53 -0
- data/lib/rubocop/cop/hecks/no_wall_clock_in_runtime.rb +34 -0
- data/lib/rubocop/cop/hecks/raw_shell_interpolation.rb +79 -0
- data/lib/rubocop/cop/hecks/sleep_in_spec.rb +34 -0
- data/lib/rubocop/cop/hecks/swallowed_rescue.rb +37 -0
- data/lib/rubocop/cop/hecks/symbol_string_key_mix.rb +50 -0
- data/lib/rubocop/cop/hecks/untyped_raise.rb +37 -0
- data/prebuilt/05c358d38d661a3774f813d49c66f5b68939af94432d66257a56f49ee2598994.marshal +0 -0
- data/prebuilt/26c40c61f09b09b11651c64668eda3ceb180f83cf2a3802d3033d2fd99ac841b.marshal +0 -0
- data/prebuilt/verdicts-230ac18c25abfd3d92ede032bee2661a4c2cd2773ab0cf0e2ae52546ba2f7af9.json +1 -0
- data/rust/.gitignore +6 -0
- data/rust/Cargo.toml +9 -1
- data/rust/build/src/binding_pass.rs +103 -0
- data/rust/build/src/cargo_sync.rs +6 -0
- data/rust/build/src/lineage_pass.rs +3 -3
- data/rust/build/src/main.rs +17 -5
- data/rust/build/src/pipeline.rs +48 -5
- data/rust/build/src/seams_pass.rs +123 -0
- data/rust/build/src/translation_pass.rs +167 -0
- data/rust/codegen/src/asks.rs +103 -0
- data/rust/codegen/src/bridging.rs +1 -1
- data/rust/codegen/src/commands.rs +13 -8
- data/rust/codegen/src/constraints.rs +25 -0
- data/rust/codegen/src/domain_generator.rs +11 -9
- data/rust/codegen/src/expr_emitter.rs +13 -0
- data/rust/codegen/src/json_codec.rs +54 -15
- data/rust/codegen/src/main.rs +2 -1
- data/rust/codegen/src/mutations.rs +174 -4
- data/rust/codegen/src/naming.rs +30 -0
- data/rust/codegen/src/queries.rs +11 -2
- data/rust/codegen/src/reactions.rs +1 -1
- data/rust/codegen/src/types.rs +166 -8
- data/rust/host/HECKS_RELEASE +1 -1
- data/rust/host/src/api.rs +4 -4
- data/rust/host/src/bin/validate_harness.rs +43 -0
- data/rust/host/src/boundary_fuzz/expr_json.rs +2 -2
- data/rust/host/src/boundary_fuzz/ir.rs +1 -0
- data/rust/host/src/boundary_fuzz/server.rs +1 -1
- data/rust/host/src/checkout.rs +35 -19
- data/rust/host/src/commerce_ir.rs +408 -2
- data/rust/host/src/expr_json.rs +118 -0
- data/rust/host/src/mint.rs +1 -1
- data/rust/host/src/payments/tests.rs +27 -1
- data/rust/host/src/payments.rs +12 -4
- data/rust/host/src/query_step.rs +22 -4
- data/rust/host/src/reference_validate.rs +653 -108
- data/rust/host/src/resend.rs +2 -0
- data/rust/host/src/server.rs +19 -6
- data/rust/host/src/ui_schema.rs +132 -5
- data/rust/host/src/web/newsletter.rs +7 -8
- data/rust/host/src/web/newsletter_send.rs +38 -13
- data/rust/host/src/web/registrations.rs +229 -26
- data/rust/host/src/web.rs +3 -3
- data/rust/parser/src/emit.rs +38 -6
- data/rust/parser/src/expr/ast_json.rs +7 -1
- data/rust/parser/src/expr/resolver.rs +171 -6
- data/rust/parser/src/ir.rs +7 -0
- data/rust/parser/src/keywords.rs +24 -0
- data/rust/parser/src/main.rs +3 -0
- data/rust/parser/src/parse/aggregate.rs +2 -1
- data/rust/parser/src/parse/chapter.rs +2 -2
- data/rust/parser/src/parse/command.rs +2 -0
- data/rust/parser/src/parse/hecksagon.rs +5 -2
- data/rust/parser/src/parse/lifecycle.rs +81 -0
- data/rust/parser/src/parse/mod.rs +15 -1
- data/rust/parser/src/parse/policy.rs +13 -2
- data/rust/parser/src/parse/value_object.rs +13 -8
- data/rust/parser/src/ruby_value.rs +1 -1
- data/rust/src/exemplar/commands.rs +4 -4
- data/rust/src/exemplar/constraints.rs +1 -1
- data/rust/src/exemplar/json.rs +43 -1
- data/rust/src/kernel/cli.rs +7 -5
- data/rust/src/kernel/dispatch.rs +2 -2
- data/rust/src/kernel/dispatch_guard_tests.rs +1 -1
- data/rust/src/kernel/expr.rs +73 -4
- data/rust/src/kernel/expression_operators/arithmetic.rs +130 -5
- data/rust/src/kernel/expression_operators/presence.rs +41 -3
- data/rust/src/kernel/expression_operators/text.rs +26 -2
- data/rust/src/kernel/json.rs +15 -0
- data/rust/src/kernel/mod.rs +1 -1
- data/rust/src/kernel/orchestrate.rs +54 -5
- data/rust/src/kernel/orchestrate_guard_tests.rs +67 -0
- data/rust/src/kernel/pattern.rs +60 -0
- data/rust/src/kernel/reference_lookup.rs +90 -7
- metadata +174 -36
- data/lib/hecks/doors/cli_door/nesting.rb +0 -34
- data/lib/hecks/doors/cli_door/shorthand.rb +0 -85
- data/lib/hecks/doors/cli_door.rb +0 -124
- data/lib/hecks/doors/cli_runner/answers.rb +0 -138
- data/lib/hecks/doors/cli_runner/resolution.rb +0 -146
- data/lib/hecks/doors/cli_runner/settling.rb +0 -115
- data/lib/hecks/doors/cli_runner/streaming.rb +0 -90
- data/lib/hecks/doors/cli_runner/suggestions.rb +0 -43
- data/lib/hecks/doors/cli_runner.rb +0 -66
- data/lib/hecks/doors/command_request.rb +0 -119
- data/lib/hecks/doors/handle.rb +0 -208
- data/lib/hecks/doors/json_door.rb +0 -132
- data/lib/hecks/doors/launcher_options.rb +0 -247
- data/lib/hecks/doors/mcp_door/properties.rb +0 -47
- data/lib/hecks/doors/mcp_door/tool_calls.rb +0 -113
- data/lib/hecks/doors/mcp_door/tool_definitions/auditing.rb +0 -51
- data/lib/hecks/doors/mcp_door/tool_definitions/calling.rb +0 -69
- data/lib/hecks/doors/mcp_door/tool_definitions/reading.rb +0 -97
- data/lib/hecks/doors/mcp_door/tool_definitions.rb +0 -15
- data/lib/hecks/doors/mcp_door.rb +0 -156
- data/lib/hecks/doors/mcp_door_scope/argument_policy.rb +0 -92
- data/lib/hecks/doors/mcp_door_scope/env_problems.rb +0 -55
- data/lib/hecks/doors/mcp_door_scope/presenter.rb +0 -78
- data/lib/hecks/doors/mcp_door_scope.rb +0 -201
- data/lib/hecks/doors/mcp_guide_cache.rb +0 -75
- data/lib/hecks/doors/ruby_door/aggregate_door.rb +0 -162
- data/lib/hecks/doors/ruby_door/chapter.rb +0 -89
- data/lib/hecks/doors/ruby_door.rb +0 -80
- data/lib/hecks/doors/usage_cache.rb +0 -139
- data/prebuilt/0ee7f83cba59ce7d1c45c65ba166b071779f70143449f584f33ff7989f64eccb.marshal +0 -0
- data/prebuilt/6e4a6bcf0aa880e2e6b056e1846b70e5a768090b9dc998bbadeaf5d6bcfd3db8.marshal +0 -0
- data/prebuilt/verdicts-91aaf93ce8981bd9a754948f075d95650dc2ec6435015376f5c60992b36a24b1.json +0 -1
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
require_relative "../.."
|
|
3
|
+
require_relative "../../cli/mcp"
|
|
4
|
+
require_relative "mcp/tool_definitions"
|
|
5
|
+
require_relative "mcp/tool_calls"
|
|
6
|
+
|
|
7
|
+
module Hecks
|
|
8
|
+
module Adapters
|
|
9
|
+
module Driving
|
|
10
|
+
# The MCP server onto the storehouse bus: hand-rolled JSON-RPC over stdio, one
|
|
11
|
+
# server per booted domain. All logic and the JSON shape answered with live in
|
|
12
|
+
# `Storehouse`; this is plumbing only. A `role:`/`actor_id:` a caller sends is
|
|
13
|
+
# asserted, not verified, unless Governance is attached.
|
|
14
|
+
#
|
|
15
|
+
# There is no authentication: `domain:` boots real Ruby confined to
|
|
16
|
+
# `Storehouse::BOOT_ROOT`, so this is a code execution surface for anyone who
|
|
17
|
+
# can write to stdin (ADR 0062). Spawning with `HECKS_SERVER_TOOLS=readers` and
|
|
18
|
+
# `HECKS_SERVER_DOMAINS=<dir>[:<dir>...]` narrows a server to read-only tools and
|
|
19
|
+
# named domains (`McpScope`); this limits what one agent can reach and is
|
|
20
|
+
# not itself authentication.
|
|
21
|
+
module Mcp
|
|
22
|
+
PROTOCOL_VERSION = "2024-11-05".freeze
|
|
23
|
+
|
|
24
|
+
TOOLS = ToolDefinitions::TOOLS
|
|
25
|
+
|
|
26
|
+
NOTES = [
|
|
27
|
+
"Identity is self-asserted: role:/actor_id: are claims made by the caller and are not verified.",
|
|
28
|
+
"state, events, history, follow, describe, catalog and query run with no role check at all.",
|
|
29
|
+
"domain: boots real Ruby under #{Storehouse::BOOT_ROOT}; anyone who can write to stdin can run it."
|
|
30
|
+
].freeze
|
|
31
|
+
|
|
32
|
+
# The method that answers each JSON-RPC method that has a response.
|
|
33
|
+
REPLIES = {
|
|
34
|
+
"initialize" => :initialize_result,
|
|
35
|
+
"tools/list" => :tools_list,
|
|
36
|
+
"tools/call" => :tools_call,
|
|
37
|
+
"ping" => :ping_result
|
|
38
|
+
}.freeze
|
|
39
|
+
|
|
40
|
+
module_function
|
|
41
|
+
|
|
42
|
+
# Read once, so a bad setting refuses to start the server before anything is
|
|
43
|
+
# written to stdout.
|
|
44
|
+
def scope
|
|
45
|
+
@scope ||= McpScope.start!(server: CLI::Mcp::SERVER)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# The standing notes, then what this server's scope says about `domain:`.
|
|
49
|
+
def warning_notes
|
|
50
|
+
NOTES[0, 2] + (scope.restricted? ? scope.notes : [NOTES[2]])
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def serve
|
|
54
|
+
McpStdioGuard.warn!(server: CLI::Mcp::SERVER, notes: warning_notes)
|
|
55
|
+
$stdout.sync = true
|
|
56
|
+
|
|
57
|
+
$stdin.each_line { |line| answer_line(line.strip) }
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Answers one request line; a line that is not JSON is a parse error, not a crash.
|
|
61
|
+
def answer_line(line)
|
|
62
|
+
return if line.empty?
|
|
63
|
+
|
|
64
|
+
handle_safely(JSON.parse(line))
|
|
65
|
+
rescue JSON::ParserError => e
|
|
66
|
+
send_error(nil, -32_700, "Parse error: #{e.message}")
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Handles a request, answering an internal error rather than ending the server.
|
|
70
|
+
def handle_safely(request)
|
|
71
|
+
handle(request)
|
|
72
|
+
rescue StandardError => e
|
|
73
|
+
send_error(request["id"], -32_603, "Internal error: #{e.class}: #{e.message}")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# `domain:` is confined to `Storehouse::BOOT_ROOT`, and further to a restricted
|
|
77
|
+
# server's `HECKS_SERVER_DOMAINS`, before anything boots. An unrestricted server boots the
|
|
78
|
+
# domain afresh on every call; a restricted server lives for one spawner's session and
|
|
79
|
+
# keeps each named domain booted until the files of its directory change.
|
|
80
|
+
def boot(domain)
|
|
81
|
+
path = Storehouse.confine!(scope.admit_domain!(domain), "domain")
|
|
82
|
+
runtime = scope.restricted? ? resident_boot(path) : Hecks.boot(path, install_driving: false)
|
|
83
|
+
Admission.admit!(runtime.registry, "Mcp")
|
|
84
|
+
runtime
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# The runtime a restricted server keeps for `path`, booted again once its files change.
|
|
88
|
+
def resident_boot(path)
|
|
89
|
+
resident = (@resident ||= {})
|
|
90
|
+
fingerprint = Storehouse.fingerprint(path)
|
|
91
|
+
return resident[path][:runtime] if resident[path] && resident[path][:fingerprint] == fingerprint
|
|
92
|
+
|
|
93
|
+
resident[path] = { fingerprint: fingerprint, runtime: Hecks.boot(path, install_driving: false) }
|
|
94
|
+
resident[path][:runtime]
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def answer_tool(name, args)
|
|
98
|
+
return scope.refusal(name) unless scope.permits_tool?(name)
|
|
99
|
+
|
|
100
|
+
ToolCalls.call(name, scope.with_default_domain(args))
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# One line per allowed command: what it takes and the role it declares, read from the booted
|
|
104
|
+
# domain the server serves. Nil when the server serves no single domain or it will not boot,
|
|
105
|
+
# so
|
|
106
|
+
# `tools/list` still answers, with the commands as a bare list.
|
|
107
|
+
def command_guide
|
|
108
|
+
return unless scope.commands_mode? && scope.default_domain
|
|
109
|
+
|
|
110
|
+
McpGuideCache.remember(scope.default_domain, scope.allowed_commands) do
|
|
111
|
+
Storehouse.command_guide(boot(scope.default_domain), scope.allowed_commands)
|
|
112
|
+
end
|
|
113
|
+
rescue StandardError
|
|
114
|
+
nil
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def tool_result(payload)
|
|
118
|
+
{ content: [{ type: "text", text: JSON.pretty_generate(payload) }], isError: payload[:ok] == false }
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def send_response(id, result)
|
|
122
|
+
puts JSON.generate({ jsonrpc: "2.0", id: id, result: result })
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def send_error(id, code, message)
|
|
126
|
+
puts JSON.generate({ jsonrpc: "2.0", id: id, error: { code: code, message: message } })
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Answers one JSON-RPC request. A notification has no id and gets no response.
|
|
130
|
+
def handle(request)
|
|
131
|
+
id = request["id"]
|
|
132
|
+
method = request["method"]
|
|
133
|
+
reply = REPLIES[method]
|
|
134
|
+
return send_response(id, public_send(reply, request["params"] || {})) if reply
|
|
135
|
+
|
|
136
|
+
send_error(id, -32_601, "Method not found: #{method}") unless id.nil? || method == "notifications/initialized"
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def initialize_result(_params)
|
|
140
|
+
{ protocolVersion: PROTOCOL_VERSION, capabilities: { tools: {} },
|
|
141
|
+
serverInfo: { name: CLI::Mcp::SERVER, version: "1.1.0" } }
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# The tools this server's scope permits, each presented with the command guide.
|
|
145
|
+
def tools_list(_params)
|
|
146
|
+
served = TOOLS.select { |tool| scope.permits_tool?(tool[:name]) }
|
|
147
|
+
guide = command_guide
|
|
148
|
+
{ tools: served.map { |tool| scope.present(tool, guide) } }
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def tools_call(params)
|
|
152
|
+
tool_result(answer_tool(params["name"], params["arguments"] || {}))
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def ping_result(_params) = {}
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
require "digest"
|
|
2
|
+
require "fileutils"
|
|
3
|
+
require "json"
|
|
4
|
+
require_relative "../../cache_dir"
|
|
5
|
+
require_relative "../../storehouse"
|
|
6
|
+
require_relative "../../bluebook/meta_validator/verdict_cache"
|
|
7
|
+
|
|
8
|
+
module Hecks
|
|
9
|
+
module Adapters
|
|
10
|
+
module Driving
|
|
11
|
+
# Remembers the command guide a commands server builds for `tools/list`, so a server that
|
|
12
|
+
# starts
|
|
13
|
+
# again does not boot its domain to describe its commands. A client waits only a moment for a
|
|
14
|
+
# server's tools, and a boot at that point costs more than the wait.
|
|
15
|
+
#
|
|
16
|
+
# An entry is keyed by the domain directory's files, a digest of the hecks code, and the
|
|
17
|
+
# allowed commands, so a change to any of them builds a new guide. The file is JSON, read only
|
|
18
|
+
# from a directory and a file that only this user can write (the verdict cache's own check),
|
|
19
|
+
# and only when it is a list of strings: a guide is text an agent reads as instructions.
|
|
20
|
+
module McpGuideCache
|
|
21
|
+
module_function
|
|
22
|
+
|
|
23
|
+
# The guide for a domain and its allowed commands, from the cache or else from the block,
|
|
24
|
+
# which is then remembered. A cache that cannot be written is not an error: the next start
|
|
25
|
+
# builds the guide again.
|
|
26
|
+
#
|
|
27
|
+
# @param domain [String] the domain directory the guide describes
|
|
28
|
+
# @param commands [Array<String>] the allowed command names
|
|
29
|
+
# @yieldreturn [Array<String>, nil] the guide, built when none is remembered
|
|
30
|
+
# @return [Array<String>, nil] the guide
|
|
31
|
+
def remember(domain, commands)
|
|
32
|
+
file = File.join(dir, "#{key(domain, commands)}.json")
|
|
33
|
+
cached = read(file)
|
|
34
|
+
return cached if cached
|
|
35
|
+
|
|
36
|
+
guide = yield
|
|
37
|
+
write(file, guide) if guide.is_a?(Array) && guide.any?
|
|
38
|
+
guide
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# @return [String] the directory entries live in
|
|
42
|
+
def dir = Hecks::CacheDir.path("mcp_guides")
|
|
43
|
+
|
|
44
|
+
# @param domain [String] the domain directory
|
|
45
|
+
# @param commands [Array<String>] the allowed command names
|
|
46
|
+
# @return [String] a hex SHA-256 over the domain's files, the hecks code and the commands
|
|
47
|
+
def key(domain, commands)
|
|
48
|
+
parts = [Storehouse.fingerprint(domain), Bluebook::MetaValidator::VerdictCache.code_digest,
|
|
49
|
+
commands.sort.join(",")]
|
|
50
|
+
Digest::SHA256.hexdigest(parts.join("\0"))
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @param file [String] an entry's path
|
|
54
|
+
# @return [Array<String>, nil] the guide, or nil when absent, untrusted or malformed
|
|
55
|
+
def read(file)
|
|
56
|
+
return unless File.file?(file) && Bluebook::MetaValidator::VerdictCache.trusted?(file)
|
|
57
|
+
|
|
58
|
+
guide = JSON.parse(File.read(file))
|
|
59
|
+
guide if guide.is_a?(Array) && guide.all?(String)
|
|
60
|
+
rescue StandardError
|
|
61
|
+
nil
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# @param file [String] an entry's path
|
|
65
|
+
# @param guide [Array<String>] the guide to remember
|
|
66
|
+
# @return [void]
|
|
67
|
+
def write(file, guide)
|
|
68
|
+
FileUtils.mkdir_p(dir, mode: 0o700)
|
|
69
|
+
temp = "#{file}.#{Process.pid}.tmp"
|
|
70
|
+
File.write(temp, JSON.generate(guide), perm: 0o600)
|
|
71
|
+
File.rename(temp, file)
|
|
72
|
+
rescue StandardError
|
|
73
|
+
FileUtils.rm_f(temp) if temp
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
require_relative "../../../runtime/errors"
|
|
2
|
+
|
|
3
|
+
module Hecks
|
|
4
|
+
module Adapters
|
|
5
|
+
module Driving
|
|
6
|
+
class McpScope
|
|
7
|
+
# What a restricted server lets an argument say: a denied name is refused, a path must
|
|
8
|
+
# resolve
|
|
9
|
+
# inside the root with symlinks followed, and a git ref must be a plain name.
|
|
10
|
+
module ArgumentPolicy
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
# Checks every argument in `args`, nested or not, and refuses the first one the policy
|
|
14
|
+
# forbids.
|
|
15
|
+
#
|
|
16
|
+
# @param args [Hash, Array, nil] the arguments as the caller gave them
|
|
17
|
+
# @param label [String] how the server describes its mode, for the refusal
|
|
18
|
+
# @raise [Runtime::TypeMismatch] on the first argument refused
|
|
19
|
+
def admit!(args, label)
|
|
20
|
+
each_argument(args) do |name, value|
|
|
21
|
+
if DENIED_ARGUMENTS.include?(name)
|
|
22
|
+
refuse!(name, label, "never passes it on: it names a host, a binary, an output or a change of state")
|
|
23
|
+
end
|
|
24
|
+
admit_path!(name, value, label) if PATH_ARGUMENTS.include?(name)
|
|
25
|
+
admit_ref!(name, value, label) if REF_ARGUMENTS.include?(name)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Yields every argument name with its value, descending into nested objects and lists, so
|
|
30
|
+
# `{"file" => {"value" => "x"}}` is checked the way `{"file" => "x"}` is.
|
|
31
|
+
def each_argument(args, &visit)
|
|
32
|
+
case args
|
|
33
|
+
when Hash
|
|
34
|
+
args.each do |name, value|
|
|
35
|
+
yield(name.to_s, value)
|
|
36
|
+
each_argument(value, &visit)
|
|
37
|
+
end
|
|
38
|
+
when Array
|
|
39
|
+
args.each { |item| each_argument(item, &visit) }
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# A value written as an object of one field (`{"value" => "x"}`) is that field's value.
|
|
44
|
+
def scalar(value)
|
|
45
|
+
return value unless value.is_a?(Hash)
|
|
46
|
+
|
|
47
|
+
value.fetch("value") { value.fetch(:value, value) }
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def refuse!(name, label, why)
|
|
51
|
+
raise Runtime::TypeMismatch, "argument: #{name.inspect} is refused: this server runs in #{label} and #{why}"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def admit_path!(name, value, label)
|
|
55
|
+
Array(scalar(value)).flat_map { |item| item.to_s.split(",") }.reject(&:empty?).each do |path|
|
|
56
|
+
next if !path.include?(":") && inside_root?(path)
|
|
57
|
+
|
|
58
|
+
refuse!(name, label, "passes a path only when it resolves inside #{Storehouse::BOOT_ROOT} " \
|
|
59
|
+
"and holds no colon: #{path.inspect}")
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def admit_ref!(name, value, label)
|
|
64
|
+
ref = scalar(value).to_s
|
|
65
|
+
return if ref.match?(PLAIN_REF) && !ref.include?("..")
|
|
66
|
+
|
|
67
|
+
refuse!(name, label, "passes only a plain git ref: #{ref.inspect}")
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def inside_root?(path)
|
|
71
|
+
root = resolve_real(Storehouse::BOOT_ROOT)
|
|
72
|
+
resolved = resolve_real(File.expand_path(path, Storehouse::BOOT_ROOT))
|
|
73
|
+
resolved == root || resolved.start_with?("#{root}#{File::SEPARATOR}")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# The real path of `path`, symlinks followed: of the whole path when it exists, else of
|
|
77
|
+
# its
|
|
78
|
+
# deepest existing parent with the missing names put back, so a link above a file that
|
|
79
|
+
# does
|
|
80
|
+
# not exist yet still shows where the file would land.
|
|
81
|
+
def resolve_real(path)
|
|
82
|
+
missing = []
|
|
83
|
+
current = File.expand_path(path)
|
|
84
|
+
until File.exist?(current)
|
|
85
|
+
parent = File.dirname(current)
|
|
86
|
+
break if parent == current
|
|
87
|
+
|
|
88
|
+
missing.unshift(File.basename(current))
|
|
89
|
+
current = parent
|
|
90
|
+
end
|
|
91
|
+
File.join(File.realpath(current), *missing)
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Adapters
|
|
3
|
+
module Driving
|
|
4
|
+
class McpScope
|
|
5
|
+
# What is wrong with the `HECKS_SERVER_*` variables a server was spawned with, as the
|
|
6
|
+
# sentences that refuse to start it.
|
|
7
|
+
module EnvProblems
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
# Variables under this prefix are retired spellings of the `HECKS_SERVER_*` ones.
|
|
11
|
+
RETIRED_PREFIX = "HECKS_DOOR_".freeze
|
|
12
|
+
|
|
13
|
+
# Every problem in `env`: a variable the server does not take, then those of the mode.
|
|
14
|
+
def setting_problems(env)
|
|
15
|
+
known = [TOOLS_VARIABLE, DOMAINS_VARIABLE, COMMANDS_VARIABLE]
|
|
16
|
+
problems = retired_problems(env) + (env.keys.select { |name| name.start_with?(ENV_PREFIX) } - known).map do |name|
|
|
17
|
+
"environment #{name} is not accepted; the server takes only #{known.join(", ")}"
|
|
18
|
+
end
|
|
19
|
+
problems + mode_problems(env[TOOLS_VARIABLE], env[DOMAINS_VARIABLE], env[COMMANDS_VARIABLE])
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# A variable under the retired prefix is refused, never ignored: ignoring
|
|
23
|
+
# `HECKS_DOOR_TOOLS`
|
|
24
|
+
# would start an unrestricted server where the spawner asked for a narrow one.
|
|
25
|
+
def retired_problems(env)
|
|
26
|
+
env.keys.select { |name| name.start_with?(RETIRED_PREFIX) }.map do |name|
|
|
27
|
+
"environment #{name} is retired; the server reads #{name.sub(RETIRED_PREFIX, ENV_PREFIX)}"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def mode_problems(tools, domains, commands)
|
|
32
|
+
return stray_problems(domains, commands) if tools.nil?
|
|
33
|
+
unless MODES.include?(tools)
|
|
34
|
+
return ["#{TOOLS_VARIABLE}=#{tools.inspect} is not accepted; the values are #{MODES.map(&:inspect).join(" and ")}"]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
problems = []
|
|
38
|
+
if split_paths(domains.to_s).empty?
|
|
39
|
+
problems << "#{TOOLS_VARIABLE}=#{tools} needs #{DOMAINS_VARIABLE}, the domains this server may read"
|
|
40
|
+
end
|
|
41
|
+
problems + command_problems(tools, commands)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def stray_problems(domains, commands)
|
|
45
|
+
stray = { DOMAINS_VARIABLE => domains, COMMANDS_VARIABLE => commands }.compact.keys
|
|
46
|
+
return [] if stray.empty?
|
|
47
|
+
|
|
48
|
+
["#{stray.join(" and ")} set without #{TOOLS_VARIABLE}=#{READERS} or #{TOOLS_VARIABLE}=#{COMMANDS}; " \
|
|
49
|
+
"they only narrow a restricted server"]
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def command_problems(tools, commands)
|
|
53
|
+
if tools == COMMANDS && split_names(commands).empty?
|
|
54
|
+
["#{TOOLS_VARIABLE}=#{COMMANDS} needs #{COMMANDS_VARIABLE}, the commands this server may dispatch"]
|
|
55
|
+
elsif tools == READERS && !commands.nil?
|
|
56
|
+
["#{COMMANDS_VARIABLE} is set in reader mode; it only narrows a commands server"]
|
|
57
|
+
else
|
|
58
|
+
[]
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def split_paths(value) = value.to_s.split(File::PATH_SEPARATOR).reject(&:empty?)
|
|
63
|
+
|
|
64
|
+
def split_names(value) = value.to_s.split(",").map(&:strip).reject(&:empty?).uniq
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
module Hecks
|
|
2
|
+
module Adapters
|
|
3
|
+
module Driving
|
|
4
|
+
class McpScope
|
|
5
|
+
# A tool definition as `tools/list` shows it to one server, and the notes the server starts
|
|
6
|
+
# with.
|
|
7
|
+
module Presenter
|
|
8
|
+
module_function
|
|
9
|
+
|
|
10
|
+
# The tool as `tools/list` shows it. A server that serves one domain makes `domain:`
|
|
11
|
+
# optional.
|
|
12
|
+
# On a commands server, `dispatch` also names the commands it serves as an enum, so a
|
|
13
|
+
# caller
|
|
14
|
+
# sees them as typed choices instead of finding out by being refused, and says what each
|
|
15
|
+
# takes and which role it declares.
|
|
16
|
+
#
|
|
17
|
+
# @param scope [McpScope] the server's scope
|
|
18
|
+
# @param tool [Hash] a tool definition from `Mcp::TOOLS`
|
|
19
|
+
# @param guide [Array<String>, nil] one line per allowed command
|
|
20
|
+
# (`Storehouse.command_guide`)
|
|
21
|
+
# @return [Hash] the definition, narrowed for this server
|
|
22
|
+
def present(scope, tool, guide)
|
|
23
|
+
tool = domain_optional(tool, scope.default_domain) if scope.default_domain
|
|
24
|
+
return tool unless scope.commands_mode? && tool[:name] == "dispatch"
|
|
25
|
+
|
|
26
|
+
narrow_dispatch(tool, guide, scope.allowed_commands)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# `dispatch`, with the commands the server serves as typed choices.
|
|
30
|
+
def narrow_dispatch(tool, guide, commands)
|
|
31
|
+
properties = tool[:inputSchema][:properties]
|
|
32
|
+
steps = properties[:steps]
|
|
33
|
+
narrowed = properties.merge(command: with_enum(properties[:command], commands),
|
|
34
|
+
steps: steps.merge(items: step_items(steps[:items], commands)))
|
|
35
|
+
tool.merge(description: dispatch_description(tool[:description], guide, commands),
|
|
36
|
+
inputSchema: tool[:inputSchema].merge(properties: narrowed))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def domain_optional(tool, default)
|
|
40
|
+
schema = tool[:inputSchema]
|
|
41
|
+
return tool unless schema[:properties].key?(:domain)
|
|
42
|
+
|
|
43
|
+
note = "Optional: this server serves one domain, #{default}, and uses it when you leave this out."
|
|
44
|
+
properties = schema[:properties].merge(domain: schema[:properties][:domain].merge(description: note))
|
|
45
|
+
tool.merge(inputSchema: schema.merge(properties: properties, required: schema[:required] - ["domain"]))
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def dispatch_description(base, guide, commands)
|
|
49
|
+
intro = "#{base} This server dispatches only the commands below, each with the role it declares: pass " \
|
|
50
|
+
"that role as `role`. A command that takes `run` takes a key you choose, and the call answers " \
|
|
51
|
+
"the record as it stands once its reactions have run; an argument marked * is required."
|
|
52
|
+
entries = Array(guide).empty? ? commands : guide
|
|
53
|
+
([intro] + entries.map { |entry| "- #{entry}" }).join("\n")
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def with_enum(property, commands)
|
|
57
|
+
property.merge(enum: commands)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def step_items(items, commands)
|
|
61
|
+
items.merge(properties: items[:properties].merge(command: with_enum(items[:properties][:command], commands)))
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# What a reader server says about itself when it starts.
|
|
65
|
+
def reader_notes(domains)
|
|
66
|
+
["Reader mode (#{TOOLS_VARIABLE}=#{READERS}): serves #{READER_TOOLS.join(", ")}; " \
|
|
67
|
+
"refuses dispatch (with dry_run and steps), behaviors and every other tool.",
|
|
68
|
+
"domain: boots only #{DOMAINS_VARIABLE}: #{domains.join(", ")}.",
|
|
69
|
+
"Reader mode limits reach and identifies no one; it is not authentication."]
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# What a commands server says about itself when it starts.
|
|
73
|
+
def commands_notes(domains, commands)
|
|
74
|
+
["Commands mode (#{TOOLS_VARIABLE}=#{COMMANDS}): serves #{READER_TOOLS.join(", ")} and dispatch of only " \
|
|
75
|
+
"#{COMMANDS_VARIABLE}: #{commands.join(", ")}; refuses behaviors and every other tool.",
|
|
76
|
+
"domain: boots only #{DOMAINS_VARIABLE}: #{domains.join(", ")}.",
|
|
77
|
+
"Commands mode limits reach and identifies no one; role: stays self-asserted and it is not authentication."]
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|