open-loam 0.1.1 → 0.2.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/CHANGELOG.md +32 -8
- data/LICENSE +1 -1
- data/README.md +24 -24
- data/app/jobs/{loam → open_loam}/custom_field_reindex_job.rb +6 -6
- data/app/jobs/{loam → open_loam}/event_delivery_job.rb +8 -8
- data/app/jobs/{loam → open_loam}/event_redelivery_sweep_job.rb +4 -4
- data/app/jobs/{loam → open_loam}/webhook_delivery_job.rb +7 -7
- data/app/models/{loam → open_loam}/api_token.rb +7 -7
- data/app/models/{loam → open_loam}/audit_record.rb +3 -3
- data/app/models/{loam → open_loam}/auth_attempt.rb +5 -5
- data/app/models/{loam → open_loam}/business_rule.rb +10 -10
- data/app/models/{loam → open_loam}/business_rule_run.rb +5 -5
- data/app/models/{loam → open_loam}/comment.rb +9 -9
- data/app/models/{loam → open_loam}/config.rb +7 -7
- data/app/models/{loam → open_loam}/custom_field_value.rb +5 -5
- data/app/models/{loam → open_loam}/dashboard_widget.rb +5 -5
- data/app/models/open_loam/dictionary.rb +20 -0
- data/app/models/{loam → open_loam}/dictionary_entry.rb +7 -7
- data/app/models/{loam → open_loam}/event_delivery.rb +4 -4
- data/app/models/{loam → open_loam}/field_definition.rb +8 -8
- data/app/models/{loam → open_loam}/inbound_webhook_delivery.rb +4 -4
- data/app/models/{loam → open_loam}/inbound_webhook_source.rb +13 -13
- data/app/models/{loam → open_loam}/membership.rb +6 -6
- data/app/models/{loam → open_loam}/mfa_credential.rb +8 -8
- data/app/models/{loam → open_loam}/notification.rb +10 -10
- data/app/models/{loam → open_loam}/pending_action.rb +23 -23
- data/app/models/{loam → open_loam}/perspective.rb +5 -5
- data/app/models/{loam → open_loam}/progress_job.rb +7 -7
- data/app/models/{loam → open_loam}/record_lock.rb +5 -5
- data/app/models/{loam → open_loam}/scheduled_job.rb +7 -7
- data/app/models/open_loam/search_token.rb +9 -0
- data/app/models/{loam → open_loam}/sso_identity.rb +4 -4
- data/app/models/{loam → open_loam}/sso_provider.rb +8 -8
- data/app/models/open_loam/tenant.rb +20 -0
- data/app/models/{loam → open_loam}/translation.rb +5 -5
- data/app/models/{loam → open_loam}/webhook_endpoint.rb +6 -6
- data/app/views/{loam → open_loam}/attachments/_attachments.html.erb +1 -1
- data/app/views/{loam → open_loam}/comments/_comments.html.erb +1 -1
- data/app/views/{loam → open_loam}/custom_fields/_fields.html.erb +2 -2
- data/lib/generators/{loam → open_loam}/entity/entity_generator.rb +9 -9
- data/lib/generators/{loam → open_loam}/entity/templates/api_controller.rb +1 -1
- data/lib/generators/{loam → open_loam}/entity/templates/controller.rb +21 -21
- data/lib/generators/{loam → open_loam}/entity/templates/entity_test.rb +12 -12
- data/lib/generators/{loam → open_loam}/entity/templates/migration.rb +5 -5
- data/lib/generators/{loam → open_loam}/entity/templates/model.rb +10 -10
- data/lib/generators/{loam → open_loam}/entity/templates/policy.rb +1 -1
- data/lib/generators/{loam → open_loam}/entity/templates/views/_form.html.erb +4 -4
- data/lib/generators/{loam → open_loam}/entity/templates/views/deleted.html.erb +7 -7
- data/lib/generators/open_loam/entity/templates/views/edit.html.erb +3 -0
- data/lib/generators/open_loam/entity/templates/views/index.html.erb +90 -0
- data/lib/generators/open_loam/entity/templates/views/new.html.erb +3 -0
- data/lib/generators/open_loam/entity/templates/views/show.html.erb +36 -0
- data/lib/generators/{loam → open_loam}/install/install_generator.rb +47 -47
- data/lib/generators/open_loam/install/templates/AGENTS.md +341 -0
- data/lib/generators/{loam → open_loam}/install/templates/admin/api_docs_controller.rb +2 -2
- data/lib/generators/{loam → open_loam}/install/templates/admin/api_docs_index.html.erb +1 -1
- data/lib/generators/{loam → open_loam}/install/templates/admin/api_tokens_controller.rb +1 -1
- data/lib/generators/{loam → open_loam}/install/templates/admin/base_controller.rb +38 -38
- data/lib/generators/{loam → open_loam}/install/templates/admin/business_rules_controller.rb +7 -7
- data/lib/generators/{loam → open_loam}/install/templates/admin/business_rules_form.html.erb +2 -2
- data/lib/generators/{loam → open_loam}/install/templates/admin/comments_controller.rb +4 -4
- data/lib/generators/{loam → open_loam}/install/templates/admin/configs_controller.rb +9 -9
- data/lib/generators/open_loam/install/templates/admin/dashboard_controller.rb +10 -0
- data/lib/generators/{loam → open_loam}/install/templates/admin/dashboard_widgets_controller.rb +5 -5
- data/lib/generators/{loam → open_loam}/install/templates/admin/dictionaries_controller.rb +5 -5
- data/lib/generators/{loam → open_loam}/install/templates/admin/dictionaries_form.html.erb +1 -1
- data/lib/generators/{loam → open_loam}/install/templates/admin/dictionary_entries_controller.rb +2 -2
- data/lib/generators/{loam → open_loam}/install/templates/admin/event_deliveries_controller.rb +5 -5
- data/lib/generators/{loam → open_loam}/install/templates/admin/event_deliveries_index.html.erb +1 -1
- data/lib/generators/{loam → open_loam}/install/templates/admin/events_controller.rb +5 -5
- data/lib/generators/{loam → open_loam}/install/templates/admin/features_controller.rb +9 -9
- data/lib/generators/{loam → open_loam}/install/templates/admin/field_definitions_controller.rb +5 -5
- data/lib/generators/{loam → open_loam}/install/templates/admin/field_definitions_new.html.erb +2 -2
- data/lib/generators/{loam → open_loam}/install/templates/admin/history_controller.rb +7 -7
- data/lib/generators/{loam → open_loam}/install/templates/admin/history_index.html.erb +1 -1
- data/lib/generators/{loam → open_loam}/install/templates/admin/imports_controller.rb +12 -12
- data/lib/generators/{loam → open_loam}/install/templates/admin/inbound_webhook_sources_controller.rb +6 -6
- data/lib/generators/{loam → open_loam}/install/templates/admin/inbound_webhook_sources_new.html.erb +2 -2
- data/lib/generators/open_loam/install/templates/admin/layout.html.erb +76 -0
- data/lib/generators/{loam → open_loam}/install/templates/admin/mfa_controller.rb +6 -6
- data/lib/generators/{loam → open_loam}/install/templates/admin/notifications_controller.rb +3 -3
- data/lib/generators/{loam → open_loam}/install/templates/admin/overrides_controller.rb +3 -3
- data/lib/generators/{loam → open_loam}/install/templates/admin/overrides_index.html.erb +2 -2
- data/lib/generators/{loam → open_loam}/install/templates/admin/pending_actions_controller.rb +3 -3
- data/lib/generators/{loam → open_loam}/install/templates/admin/perspectives_controller.rb +7 -7
- data/lib/generators/{loam → open_loam}/install/templates/admin/perspectives_index.html.erb +1 -1
- data/lib/generators/open_loam/install/templates/admin/progress_jobs_controller.rb +19 -0
- data/lib/generators/{loam → open_loam}/install/templates/admin/progress_jobs_index.html.erb +1 -1
- data/lib/generators/{loam → open_loam}/install/templates/admin/record_locks_controller.rb +4 -4
- data/lib/generators/{loam → open_loam}/install/templates/admin/scheduled_jobs_controller.rb +7 -7
- data/lib/generators/{loam → open_loam}/install/templates/admin/scheduled_jobs_form.html.erb +1 -1
- data/lib/generators/{loam → open_loam}/install/templates/admin/scheduled_jobs_index.html.erb +1 -1
- data/lib/generators/{loam → open_loam}/install/templates/admin/search_controller.rb +5 -5
- data/lib/generators/{loam → open_loam}/install/templates/admin/sessions_controller.rb +32 -32
- data/lib/generators/{loam → open_loam}/install/templates/admin/sso_providers_controller.rb +6 -6
- data/lib/generators/{loam → open_loam}/install/templates/admin/sso_providers_form.html.erb +2 -2
- data/lib/generators/{loam → open_loam}/install/templates/admin/sudo_controller.rb +5 -5
- data/lib/generators/{loam → open_loam}/install/templates/admin/sudo_new.html.erb +1 -1
- data/lib/generators/{loam → open_loam}/install/templates/admin/translations_controller.rb +7 -7
- data/lib/generators/{loam → open_loam}/install/templates/admin/translations_index.html.erb +3 -3
- data/lib/generators/{loam → open_loam}/install/templates/admin/webhook_endpoints_controller.rb +4 -4
- data/lib/generators/{loam → open_loam}/install/templates/admin/webhook_endpoints_index.html.erb +1 -1
- data/lib/generators/{loam → open_loam}/install/templates/admin.css +1 -1
- data/lib/generators/{loam → open_loam}/install/templates/api_base_controller.rb +15 -15
- data/lib/generators/{loam → open_loam}/install/templates/guardrails_test.rb +17 -17
- data/lib/generators/{loam → open_loam}/install/templates/import_job.rb +7 -7
- data/lib/generators/{loam → open_loam}/install/templates/inbound_webhooks_controller.rb +4 -4
- data/lib/generators/{loam → open_loam}/install/templates/initializer.rb +74 -74
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_api_tokens.rb +15 -0
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_audit_records.rb +14 -0
- data/lib/generators/{loam/install/templates/migrations/create_loam_auth_attempts.rb → open_loam/install/templates/migrations/create_open_loam_auth_attempts.rb} +3 -3
- data/lib/generators/{loam/install/templates/migrations/create_loam_business_rules.rb → open_loam/install/templates/migrations/create_open_loam_business_rules.rb} +10 -10
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_comments.rb +13 -0
- data/lib/generators/{loam/install/templates/migrations/create_loam_configs.rb → open_loam/install/templates/migrations/create_open_loam_configs.rb} +7 -7
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_custom_field_values.rb +19 -0
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_dashboard_widgets.rb +12 -0
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_dictionaries.rb +11 -0
- data/lib/generators/{loam/install/templates/migrations/create_loam_dictionary_entries.rb → open_loam/install/templates/migrations/create_open_loam_dictionary_entries.rb} +6 -6
- data/lib/generators/{loam/install/templates/migrations/create_loam_event_deliveries.rb → open_loam/install/templates/migrations/create_open_loam_event_deliveries.rb} +5 -5
- data/lib/generators/{loam/install/templates/migrations/create_loam_field_definitions.rb → open_loam/install/templates/migrations/create_open_loam_field_definitions.rb} +4 -4
- data/lib/generators/{loam/install/templates/migrations/create_loam_inbound_webhooks.rb → open_loam/install/templates/migrations/create_open_loam_inbound_webhooks.rb} +9 -9
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_memberships.rb +11 -0
- data/lib/generators/{loam/install/templates/migrations/create_loam_mfa_credentials.rb → open_loam/install/templates/migrations/create_open_loam_mfa_credentials.rb} +4 -4
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_notifications.rb +15 -0
- data/lib/generators/{loam/install/templates/migrations/create_loam_pending_actions.rb → open_loam/install/templates/migrations/create_open_loam_pending_actions.rb} +9 -9
- data/lib/generators/{loam/install/templates/migrations/create_loam_perspectives.rb → open_loam/install/templates/migrations/create_open_loam_perspectives.rb} +5 -5
- data/lib/generators/{loam/install/templates/migrations/create_loam_progress_jobs.rb → open_loam/install/templates/migrations/create_open_loam_progress_jobs.rb} +5 -5
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_record_locks.rb +16 -0
- data/lib/generators/{loam/install/templates/migrations/create_loam_scheduled_jobs.rb → open_loam/install/templates/migrations/create_open_loam_scheduled_jobs.rb} +5 -5
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_search_tokens.rb +18 -0
- data/lib/generators/{loam/install/templates/migrations/create_loam_sso_providers.rb → open_loam/install/templates/migrations/create_open_loam_sso_providers.rb} +12 -12
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_tenants.rb +9 -0
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_translations.rb +15 -0
- data/lib/generators/open_loam/install/templates/migrations/create_open_loam_webhook_endpoints.rb +13 -0
- data/lib/generators/{loam → open_loam}/install/templates/migrations/create_users.rb +1 -1
- data/lib/generators/{loam → open_loam}/install/templates/user.rb +5 -5
- data/lib/generators/{loam → open_loam}/primary_key_options.rb +23 -23
- data/lib/open-loam.rb +2 -2
- data/lib/{loam → open_loam}/attachable.rb +4 -4
- data/lib/{loam → open_loam}/auditable.rb +23 -23
- data/lib/{loam → open_loam}/auth_throttle.rb +10 -10
- data/lib/{loam → open_loam}/base32.rb +1 -1
- data/lib/{loam → open_loam}/bulk.rb +7 -7
- data/lib/{loam → open_loam}/business_rules/actions.rb +7 -7
- data/lib/{loam → open_loam}/business_rules/condition.rb +5 -5
- data/lib/{loam → open_loam}/business_rules.rb +19 -19
- data/lib/{loam → open_loam}/commentable.rb +6 -6
- data/lib/{loam → open_loam}/configs.rb +19 -19
- data/lib/{loam → open_loam}/cron.rb +2 -2
- data/lib/{loam → open_loam}/csv.rb +2 -2
- data/lib/{loam → open_loam}/current.rb +5 -5
- data/lib/{loam → open_loam}/custom_field_index.rb +23 -23
- data/lib/{loam → open_loam}/custom_fields.rb +17 -17
- data/lib/{loam → open_loam}/dashboard.rb +5 -5
- data/lib/{loam → open_loam}/dictionaries.rb +12 -12
- data/lib/{loam → open_loam}/durable_events.rb +19 -19
- data/lib/{loam → open_loam}/encryptable.rb +33 -33
- data/lib/{loam → open_loam}/encryption/cipher.rb +3 -3
- data/lib/{loam → open_loam}/encryption/key_provider.rb +3 -3
- data/lib/{loam → open_loam}/encryption.rb +11 -11
- data/lib/open_loam/engine.rb +57 -0
- data/lib/{loam → open_loam}/enrichers.rb +5 -5
- data/lib/{loam → open_loam}/errors.rb +10 -10
- data/lib/{loam → open_loam}/eval.rb +2 -2
- data/lib/{loam → open_loam}/event_stream.rb +14 -14
- data/lib/{loam → open_loam}/eventful.rb +12 -12
- data/lib/{loam → open_loam}/events.rb +5 -5
- data/lib/{loam → open_loam}/export.rb +8 -8
- data/lib/{loam → open_loam}/features.rb +15 -15
- data/lib/{loam → open_loam}/generated_key.rb +6 -6
- data/lib/{loam → open_loam}/import.rb +12 -12
- data/lib/{loam → open_loam}/inbound_webhooks.rb +7 -7
- data/lib/{loam → open_loam}/lifecycle.rb +25 -25
- data/lib/{loam/locales/loam.en.yml → open_loam/locales/open_loam.en.yml} +5 -5
- data/lib/{loam → open_loam}/mcp/server.rb +9 -9
- data/lib/{loam → open_loam}/mcp.rb +21 -21
- data/lib/{loam → open_loam}/notifications.rb +5 -5
- data/lib/{loam → open_loam}/open_api.rb +16 -16
- data/lib/{loam → open_loam}/overrides.rb +10 -10
- data/lib/{loam → open_loam}/pending_actions.rb +9 -9
- data/lib/{loam → open_loam}/permissions.rb +8 -8
- data/lib/{loam → open_loam}/perspectives.rb +9 -9
- data/lib/{loam → open_loam}/policy.rb +6 -6
- data/lib/{loam → open_loam}/progress.rb +6 -6
- data/lib/{loam → open_loam}/record_locks.rb +9 -9
- data/lib/{loam → open_loam}/scheduler.rb +20 -20
- data/lib/{loam → open_loam}/search/driver.rb +3 -3
- data/lib/{loam → open_loam}/search/like_driver.rb +5 -5
- data/lib/{loam → open_loam}/search/token_driver.rb +15 -15
- data/lib/{loam → open_loam}/search.rb +5 -5
- data/lib/{loam → open_loam}/searchable.rb +22 -22
- data/lib/{loam → open_loam}/soft_deletable.rb +10 -10
- data/lib/{loam → open_loam}/sso/claims.rb +1 -1
- data/lib/{loam → open_loam}/sso/fake_provider.rb +1 -1
- data/lib/{loam → open_loam}/sso/http_client.rb +3 -3
- data/lib/{loam → open_loam}/sso/oidc_provider.rb +2 -2
- data/lib/{loam → open_loam}/sso.rb +17 -17
- data/lib/{loam → open_loam}/telemetry.rb +6 -6
- data/lib/{loam → open_loam}/tenant_record.rb +10 -10
- data/lib/{loam → open_loam}/test_helpers.rb +4 -4
- data/lib/{loam → open_loam}/totp.rb +3 -3
- data/lib/{loam → open_loam}/translatable.rb +24 -24
- data/lib/{loam → open_loam}/undo.rb +11 -11
- data/lib/open_loam/version.rb +3 -0
- data/lib/{loam → open_loam}/webhooks.rb +4 -4
- data/lib/{loam → open_loam}/widgets.rb +9 -9
- data/lib/{loam → open_loam}/workflow.rb +45 -45
- data/lib/open_loam.rb +122 -0
- data/lib/tasks/{loam.rake → open_loam.rake} +43 -43
- data/lib/tasks/{loam_eval.rake → open_loam_eval.rake} +7 -7
- data/lib/tasks/open_loam_mcp.rake +20 -0
- metadata +242 -242
- data/app/models/loam/dictionary.rb +0 -20
- data/app/models/loam/search_token.rb +0 -9
- data/app/models/loam/tenant.rb +0 -20
- data/lib/generators/loam/entity/templates/views/edit.html.erb +0 -3
- data/lib/generators/loam/entity/templates/views/index.html.erb +0 -90
- data/lib/generators/loam/entity/templates/views/new.html.erb +0 -3
- data/lib/generators/loam/entity/templates/views/show.html.erb +0 -36
- data/lib/generators/loam/install/templates/AGENTS.md +0 -341
- data/lib/generators/loam/install/templates/admin/dashboard_controller.rb +0 -10
- data/lib/generators/loam/install/templates/admin/layout.html.erb +0 -76
- data/lib/generators/loam/install/templates/admin/progress_jobs_controller.rb +0 -19
- data/lib/generators/loam/install/templates/migrations/create_loam_api_tokens.rb +0 -15
- data/lib/generators/loam/install/templates/migrations/create_loam_audit_records.rb +0 -14
- data/lib/generators/loam/install/templates/migrations/create_loam_comments.rb +0 -13
- data/lib/generators/loam/install/templates/migrations/create_loam_custom_field_values.rb +0 -19
- data/lib/generators/loam/install/templates/migrations/create_loam_dashboard_widgets.rb +0 -12
- data/lib/generators/loam/install/templates/migrations/create_loam_dictionaries.rb +0 -11
- data/lib/generators/loam/install/templates/migrations/create_loam_memberships.rb +0 -11
- data/lib/generators/loam/install/templates/migrations/create_loam_notifications.rb +0 -15
- data/lib/generators/loam/install/templates/migrations/create_loam_record_locks.rb +0 -16
- data/lib/generators/loam/install/templates/migrations/create_loam_search_tokens.rb +0 -18
- data/lib/generators/loam/install/templates/migrations/create_loam_tenants.rb +0 -9
- data/lib/generators/loam/install/templates/migrations/create_loam_translations.rb +0 -15
- data/lib/generators/loam/install/templates/migrations/create_loam_webhook_endpoints.rb +0 -13
- data/lib/loam/engine.rb +0 -57
- data/lib/loam/version.rb +0 -3
- data/lib/loam.rb +0 -122
- data/lib/tasks/loam_mcp.rake +0 -20
- /data/lib/generators/{loam → open_loam}/install/templates/admin/api_tokens_index.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/business_rules_edit.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/business_rules_index.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/business_rules_new.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/configs_edit.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/configs_index.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/dashboard_index.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/dashboard_widgets_index.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/dictionaries_edit.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/dictionaries_index.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/dictionaries_new.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/features_index.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/field_definitions_index.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/imports_new.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/imports_preview.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/imports_summary.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/inbound_webhook_sources_index.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/mfa_activated.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/mfa_new.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/mfa_show.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/notifications_index.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/pagination.rb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/pending_actions_index.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/scheduled_jobs_edit.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/scheduled_jobs_new.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/search_index.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/sessions_mfa_challenge.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/sessions_new.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/sso_providers_edit.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/sso_providers_index.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/sso_providers_new.html.erb +0 -0
- /data/lib/generators/{loam → open_loam}/install/templates/admin/webhook_endpoints_new.html.erb +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
module
|
|
1
|
+
module OpenLoam
|
|
2
2
|
# A thin domain event bus over ActiveSupport::Notifications.
|
|
3
3
|
#
|
|
4
4
|
# Convention: event names are `domain.thing.happened`, e.g.
|
|
@@ -7,7 +7,7 @@ module Loam
|
|
|
7
7
|
# subscribers are always tenant-aware.
|
|
8
8
|
module Events
|
|
9
9
|
NAME_FORMAT = /\A[a-z0-9_]+(\.[a-z0-9_]+){2,}\z/
|
|
10
|
-
PREFIX = "
|
|
10
|
+
PREFIX = "open_loam.event."
|
|
11
11
|
|
|
12
12
|
def self.publish(name, payload = {})
|
|
13
13
|
name = name.to_s
|
|
@@ -17,12 +17,12 @@ module Loam
|
|
|
17
17
|
|
|
18
18
|
ActiveSupport::Notifications.instrument(
|
|
19
19
|
PREFIX + name,
|
|
20
|
-
payload.merge(tenant_id:
|
|
20
|
+
payload.merge(tenant_id: OpenLoam::Current.tenant&.id, actor_id: OpenLoam::Current.actor&.id)
|
|
21
21
|
)
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
# The subscription rule, in one place: a trailing dot is a domain prefix,
|
|
25
|
-
# anything else is an exact event name.
|
|
25
|
+
# anything else is an exact event name. OpenLoam::WebhookEndpoint matches
|
|
26
26
|
# against this too, so a pattern means the same thing everywhere.
|
|
27
27
|
def self.pattern_matches?(pattern, event_name)
|
|
28
28
|
pattern = pattern.to_s
|
|
@@ -31,7 +31,7 @@ module Loam
|
|
|
31
31
|
pattern.end_with?(".") ? event_name.start_with?(pattern) : event_name == pattern
|
|
32
32
|
end
|
|
33
33
|
|
|
34
|
-
# Every
|
|
34
|
+
# Every OpenLoam event, whatever its domain — the empty prefix matches them
|
|
35
35
|
# all. Used by the webhook dispatcher, which decides per event which
|
|
36
36
|
# endpoints care.
|
|
37
37
|
def self.subscribe_all(&block)
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
require "csv"
|
|
2
2
|
|
|
3
|
-
module
|
|
3
|
+
module OpenLoam
|
|
4
4
|
# CSV export of a tenant-scoped relation, POLICY- and ENCRYPTION-aware:
|
|
5
5
|
#
|
|
6
|
-
#
|
|
6
|
+
# OpenLoam::Export.csv(Equipment.all, actor: current_actor)
|
|
7
7
|
#
|
|
8
|
-
# * only fields the actor's role may READ are columns (
|
|
8
|
+
# * only fields the actor's role may READ are columns (OpenLoam::Policy#readable?);
|
|
9
9
|
# * an ENCRYPTED column is NEVER exported in the clear — its cell is
|
|
10
10
|
# "[encrypted]" (the same redaction as the audit trail), so a bulk export can
|
|
11
11
|
# never become a plaintext dump of PII a role shouldn't see;
|
|
12
12
|
# * declared custom fields are included (a dictionary field exports its stored
|
|
13
|
-
# code, so the file round-trips back through
|
|
13
|
+
# code, so the file round-trips back through OpenLoam::Import);
|
|
14
14
|
# * tenant isolation is free — the relation is already scoped.
|
|
15
15
|
#
|
|
16
16
|
# Prototype scale: builds the CSV in memory with the stdlib CSV. A very large
|
|
@@ -37,10 +37,10 @@ module Loam
|
|
|
37
37
|
# redacted), then declared custom fields.
|
|
38
38
|
def exportable_columns(model, actor)
|
|
39
39
|
policy = policy_for(model, actor)
|
|
40
|
-
encrypted = model.respond_to?(:
|
|
40
|
+
encrypted = model.respond_to?(:open_loam_encrypted_attributes) ? model.open_loam_encrypted_attributes : []
|
|
41
41
|
# The blind-index columns behind searchable encrypted fields (e.g.
|
|
42
42
|
# email_hash) are internal HMACs — never export them either.
|
|
43
|
-
blind = model.respond_to?(:
|
|
43
|
+
blind = model.respond_to?(:open_loam_searchable_encrypted_attributes) ? model.open_loam_searchable_encrypted_attributes.map { |a| "#{a}_hash" } : []
|
|
44
44
|
|
|
45
45
|
columns = model.column_names.reject { |c| SKIP_COLUMNS.include?(c) || blind.include?(c) || c == "custom_fields" }
|
|
46
46
|
.select { |c| policy.readable?(c) }
|
|
@@ -61,14 +61,14 @@ module Loam
|
|
|
61
61
|
when :custom then (record.custom_field(column[:name]) rescue nil)
|
|
62
62
|
else record.public_send(column[:name])
|
|
63
63
|
end
|
|
64
|
-
|
|
64
|
+
OpenLoam::Csv.safe(value) # neutralize CSV formula injection (=, +, -, @, tab/CR)
|
|
65
65
|
end
|
|
66
66
|
|
|
67
67
|
def policy_for(model, actor)
|
|
68
68
|
klass = "#{model.name}Policy".safe_constantize
|
|
69
69
|
# A blank instance as the record: readable? keys off the role, but the
|
|
70
70
|
# policy's custom-field checks read record.class.
|
|
71
|
-
(klass ||
|
|
71
|
+
(klass || OpenLoam::Policy).new(actor, model.new)
|
|
72
72
|
end
|
|
73
73
|
end
|
|
74
74
|
end
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
module
|
|
1
|
+
module OpenLoam
|
|
2
2
|
# Runtime feature toggles: is a capability turned ON for this tenant right now.
|
|
3
3
|
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
4
|
+
# OpenLoam::Features.on?(:beta_dashboard) # for the current tenant
|
|
5
|
+
# OpenLoam::Features.enable(:beta_dashboard) # this tenant only
|
|
6
|
+
# OpenLoam::Features.enable(:beta_dashboard, scope: :global) # everyone
|
|
7
|
+
# OpenLoam::Features.disable(:beta_dashboard)
|
|
8
|
+
# OpenLoam::Features.reset(:beta_dashboard) # drop the override → default
|
|
9
9
|
#
|
|
10
10
|
# A flag gates a CAPABILITY (is this feature live for the tenant), which is
|
|
11
11
|
# orthogonal to a policy — that gates a PERSON (may this user act). The two
|
|
12
12
|
# coexist: a manager may be allowed to approve reports AND the approvals
|
|
13
13
|
# feature may be switched off for their tenant during a rollout.
|
|
14
14
|
#
|
|
15
|
-
# This is deliberately a thin wrapper over
|
|
15
|
+
# This is deliberately a thin wrapper over OpenLoam::Configs: a flag is a boolean
|
|
16
16
|
# setting with the same override → global → declared-default resolution, so it
|
|
17
17
|
# reuses that store (no new table) and its per-request cache. Flags live under
|
|
18
18
|
# the reserved `features.` key prefix and get their own admin screen, because
|
|
@@ -22,7 +22,7 @@ module Loam
|
|
|
22
22
|
|
|
23
23
|
class << self
|
|
24
24
|
def on?(name)
|
|
25
|
-
!!
|
|
25
|
+
!!OpenLoam::Configs.get(key_for(name), default: default_for(name))
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def off?(name)
|
|
@@ -32,37 +32,37 @@ module Loam
|
|
|
32
32
|
# Flip a flag on. scope: :tenant (default) overrides for the current tenant
|
|
33
33
|
# and requires a tenant in context; scope: :global sets the app-wide state.
|
|
34
34
|
def enable(name, scope: :tenant)
|
|
35
|
-
|
|
35
|
+
OpenLoam::Configs.set(key_for(name), true, scope: scope)
|
|
36
36
|
end
|
|
37
37
|
|
|
38
38
|
def disable(name, scope: :tenant)
|
|
39
|
-
|
|
39
|
+
OpenLoam::Configs.set(key_for(name), false, scope: scope)
|
|
40
40
|
end
|
|
41
41
|
|
|
42
42
|
# Drop the current tenant's override so the flag falls back to the global
|
|
43
43
|
# state / declared default.
|
|
44
44
|
def reset(name)
|
|
45
|
-
|
|
45
|
+
OpenLoam::Configs.reset(key_for(name))
|
|
46
46
|
end
|
|
47
47
|
|
|
48
48
|
def overridden?(name)
|
|
49
|
-
|
|
49
|
+
OpenLoam::Configs.overridden?(key_for(name))
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
# Every declared flag, sorted — the admin lists these whether or not a row
|
|
53
53
|
# exists yet, so a flag is visible the moment it is declared.
|
|
54
54
|
def declared
|
|
55
|
-
|
|
55
|
+
OpenLoam.feature_defaults.keys.sort
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
def description(name)
|
|
59
|
-
|
|
59
|
+
OpenLoam.feature_defaults.dig(name.to_s, :description)
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
# The declared-default state of a flag (false for an unknown one), used as
|
|
63
63
|
# the last resort when there is no override and no global row.
|
|
64
64
|
def default_for(name)
|
|
65
|
-
!!
|
|
65
|
+
!!OpenLoam.feature_defaults.dig(name.to_s, :default)
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
private
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
module
|
|
1
|
+
module OpenLoam
|
|
2
2
|
# Primary-key generation for tables whose key is not an integer.
|
|
3
3
|
#
|
|
4
4
|
# Rails leaves key generation to the database, which works for integer keys
|
|
@@ -7,22 +7,22 @@ module Loam
|
|
|
7
7
|
# has neither: the INSERT sends NULL and the row is rejected or lands with an
|
|
8
8
|
# empty key.
|
|
9
9
|
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
10
|
+
# OpenLoam's tables follow the host app's key type (see
|
|
11
|
+
# OpenLoam::Generators::PrimaryKeyOptions), so on a string-keyed app every OpenLoam
|
|
12
12
|
# model needs its key generated in Ruby. Before this, hosts patched it in
|
|
13
13
|
# themselves with a global before_create on ActiveRecord::Base — a lot to ask
|
|
14
14
|
# of an app whose only deviation was not using bigints, and a hook broad
|
|
15
|
-
# enough to reach models that had nothing to do with
|
|
15
|
+
# enough to reach models that had nothing to do with OpenLoam.
|
|
16
16
|
#
|
|
17
17
|
# Integer keys take the early return, so nothing changes for the default app.
|
|
18
18
|
module GeneratedKey
|
|
19
19
|
def self.included(base)
|
|
20
|
-
base.before_create :
|
|
20
|
+
base.before_create :assign_open_loam_generated_key
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
private
|
|
24
24
|
|
|
25
|
-
def
|
|
25
|
+
def assign_open_loam_generated_key
|
|
26
26
|
key = self.class.primary_key
|
|
27
27
|
return if key.nil? # a join table declared with id: false
|
|
28
28
|
return if self[key].present? # the caller chose the key
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
require "csv"
|
|
2
2
|
require "set"
|
|
3
3
|
|
|
4
|
-
module
|
|
4
|
+
module OpenLoam
|
|
5
5
|
# CSV import mapping engine — reusable, tenant-scoped, policy-safe.
|
|
6
6
|
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
7
|
+
# OpenLoam::Import.preview(csv) # headers + first rows for the mapping UI
|
|
8
|
+
# OpenLoam::Import.run(csv, model:, mapping:, actor:, # map CSV header => field
|
|
9
9
|
# match_key: "name", # update-or-create by a key (nil = create-only)
|
|
10
10
|
# dry_run: true, # validate + report, commit nothing
|
|
11
|
-
# progress: progress_job) # advance a
|
|
11
|
+
# progress: progress_job) # advance a OpenLoam::ProgressJob per row
|
|
12
12
|
#
|
|
13
13
|
# Safety: the mapping may only target fields the actor's role can WRITE (real
|
|
14
14
|
# columns + declared custom fields); tenant_id, plumbing, and non-permitted
|
|
@@ -27,14 +27,14 @@ module Loam
|
|
|
27
27
|
|
|
28
28
|
module_function
|
|
29
29
|
|
|
30
|
-
# Resolve an entity_type string to a model — ONLY a
|
|
30
|
+
# Resolve an entity_type string to a model — ONLY a OpenLoam::TenantRecord
|
|
31
31
|
# subclass (never an arbitrary constant), so an import target can't be
|
|
32
32
|
# smuggled to a global model like User (same guard as the business-rules
|
|
33
33
|
# engine).
|
|
34
34
|
def allowed_model(entity_type)
|
|
35
35
|
klass = entity_type.to_s.safe_constantize
|
|
36
|
-
unless klass.is_a?(Class) && klass <
|
|
37
|
-
raise
|
|
36
|
+
unless klass.is_a?(Class) && klass < OpenLoam::TenantRecord
|
|
37
|
+
raise OpenLoam::Error, "import target #{entity_type.inspect} is not a OpenLoam entity"
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
klass
|
|
@@ -79,7 +79,7 @@ module Loam
|
|
|
79
79
|
is_new ? (result.created += 1) : (result.updated += 1)
|
|
80
80
|
rescue StandardError => error
|
|
81
81
|
# Store ONLY the row number + message — NEVER the raw cell values. The
|
|
82
|
-
# result is persisted (
|
|
82
|
+
# result is persisted (OpenLoam::ProgressJob.result); a failed row into an
|
|
83
83
|
# encrypted field would otherwise write PLAINTEXT PII at rest.
|
|
84
84
|
result.failed += 1
|
|
85
85
|
result.errors << { "row" => line, "message" => error.message }
|
|
@@ -103,7 +103,7 @@ module Loam
|
|
|
103
103
|
out << (Array(headers) + [ "_error" ])
|
|
104
104
|
result.errors.each do |error|
|
|
105
105
|
original = data_rows[error["row"] - 2] || [] # row is 1-based incl. the header
|
|
106
|
-
out << original.map { |cell|
|
|
106
|
+
out << original.map { |cell| OpenLoam::Csv.safe(cell) } + [ error["message"] ]
|
|
107
107
|
end
|
|
108
108
|
end
|
|
109
109
|
end
|
|
@@ -115,7 +115,7 @@ module Loam
|
|
|
115
115
|
next if target.to_s.blank?
|
|
116
116
|
next if allowed.include?(target.to_s)
|
|
117
117
|
|
|
118
|
-
raise
|
|
118
|
+
raise OpenLoam::Error, "import mapping refuses #{target.inspect} — not a writable column or custom field"
|
|
119
119
|
end
|
|
120
120
|
end
|
|
121
121
|
|
|
@@ -155,14 +155,14 @@ module Loam
|
|
|
155
155
|
def safe_parse(csv_string)
|
|
156
156
|
CSV.parse(csv_string.to_s)
|
|
157
157
|
rescue CSV::MalformedCSVError => error
|
|
158
|
-
raise
|
|
158
|
+
raise OpenLoam::Error, "could not parse CSV: #{error.message}"
|
|
159
159
|
end
|
|
160
160
|
|
|
161
161
|
def policy_for(model, actor)
|
|
162
162
|
klass = "#{model.name}Policy".safe_constantize
|
|
163
163
|
# A blank instance as the record: readable?/writable? key off the role, but
|
|
164
164
|
# custom_field_writable? reads record.class.custom_field_definitions.
|
|
165
|
-
(klass ||
|
|
165
|
+
(klass || OpenLoam::Policy).new(actor, model.new)
|
|
166
166
|
end
|
|
167
167
|
end
|
|
168
168
|
end
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
require "openssl"
|
|
2
2
|
require "digest"
|
|
3
3
|
|
|
4
|
-
module
|
|
4
|
+
module OpenLoam
|
|
5
5
|
# Receiving webhooks FROM external systems (the inbound sibling of
|
|
6
|
-
#
|
|
6
|
+
# OpenLoam::Webhooks). One entry point — `ingest` — does the whole verified,
|
|
7
7
|
# replay-resistant pipeline and returns a Result the controller turns into an
|
|
8
8
|
# HTTP status. Kept here (not in a controller) so it is testable without HTTP
|
|
9
9
|
# and shared by the generated app and the demo.
|
|
@@ -30,13 +30,13 @@ module Loam
|
|
|
30
30
|
module_function
|
|
31
31
|
|
|
32
32
|
def ingest(token:, raw_body:, headers:)
|
|
33
|
-
|
|
33
|
+
OpenLoam::Telemetry.span("inbound_webhook") { run_ingest(token, raw_body.to_s, headers) }
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
def run_ingest(token, raw_body, headers)
|
|
37
37
|
return Result.new(status: 413, reason: "body too large") if raw_body.bytesize > MAX_BYTES
|
|
38
38
|
|
|
39
|
-
source =
|
|
39
|
+
source = OpenLoam::InboundWebhookSource.resolve(token)
|
|
40
40
|
return Result.new(status: 404, reason: "unknown or inactive source") if source.nil?
|
|
41
41
|
|
|
42
42
|
signature = header(headers, source.signature_header_key)
|
|
@@ -51,8 +51,8 @@ module Loam
|
|
|
51
51
|
|
|
52
52
|
begin
|
|
53
53
|
delivery = nil
|
|
54
|
-
|
|
55
|
-
delivery =
|
|
54
|
+
OpenLoam::InboundWebhookDelivery.transaction do
|
|
55
|
+
delivery = OpenLoam::InboundWebhookDelivery.create!(
|
|
56
56
|
source: source, external_id: external_id, event_name: source.event_name,
|
|
57
57
|
status: "received", received_at: Time.current, payload: parse(raw_body)
|
|
58
58
|
)
|
|
@@ -60,7 +60,7 @@ module Loam
|
|
|
60
60
|
# lives on the delivery row, subscribers read it from there. Publishing
|
|
61
61
|
# inside the txn ties capture to the row — a publish failure rolls the
|
|
62
62
|
# row back so the sender's retry isn't deduped away.
|
|
63
|
-
|
|
63
|
+
OpenLoam::Events.publish(source.event_name, { source_id: source.id, delivery_id: delivery.id })
|
|
64
64
|
end
|
|
65
65
|
Result.new(status: 202, reason: "accepted", delivery: delivery)
|
|
66
66
|
rescue ActiveRecord::RecordNotUnique
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
module
|
|
1
|
+
module OpenLoam
|
|
2
2
|
# Tenant lifecycle: what a brand-new tenant gets for free.
|
|
3
3
|
#
|
|
4
|
-
# An app declares seeding once, in config/initializers/
|
|
4
|
+
# An app declares seeding once, in config/initializers/open_loam.rb:
|
|
5
5
|
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
6
|
+
# OpenLoam.on_tenant_created do |tenant|
|
|
7
|
+
# OpenLoam::FieldDefinition.find_or_create_by!(entity_type: "Equipment", name: "asset_tag") { ... }
|
|
8
8
|
# end
|
|
9
9
|
#
|
|
10
|
-
# The block runs inside `
|
|
10
|
+
# The block runs inside `OpenLoam.as_tenant(tenant)`, so tenant-scoped writes
|
|
11
11
|
# need no extra ceremony.
|
|
12
12
|
#
|
|
13
13
|
# THE CONTRACT: callbacks MUST be idempotent. They fire once when a tenant is
|
|
14
|
-
# created, and again for EVERY existing tenant whenever `bin/rails
|
|
14
|
+
# created, and again for EVERY existing tenant whenever `bin/rails open_loam:sync`
|
|
15
15
|
# runs — which is how a role/field/default added by a later release reaches
|
|
16
16
|
# tenants that already exist. Write `find_or_create_by!`, never `create!`.
|
|
17
17
|
module Lifecycle
|
|
@@ -27,14 +27,14 @@ module Loam
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
# The single execution path for a tenant's callbacks — used both by
|
|
30
|
-
#
|
|
30
|
+
# OpenLoam::Tenant's after_create_commit and by sync_tenants!, so "runs inside
|
|
31
31
|
# as_tenant, in declaration order" can never drift between the two.
|
|
32
32
|
#
|
|
33
33
|
# Exceptions propagate: a failing callback fails the tenant creation (Rails
|
|
34
34
|
# re-raises from after_commit) or the sync run, loudly, like every other
|
|
35
|
-
#
|
|
35
|
+
# OpenLoam guardrail.
|
|
36
36
|
def self.run_tenant_created(tenant)
|
|
37
|
-
|
|
37
|
+
OpenLoam.as_tenant(tenant) do
|
|
38
38
|
tenant_created_callbacks.each { |callback| callback.call(tenant) }
|
|
39
39
|
end
|
|
40
40
|
end
|
|
@@ -44,7 +44,7 @@ module Loam
|
|
|
44
44
|
# Returns the number of tenants synced.
|
|
45
45
|
def self.sync_tenants!
|
|
46
46
|
count = 0
|
|
47
|
-
|
|
47
|
+
OpenLoam::Tenant.find_each do |tenant|
|
|
48
48
|
run_tenant_created(tenant)
|
|
49
49
|
count += 1
|
|
50
50
|
end
|
|
@@ -52,8 +52,8 @@ module Loam
|
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
# Role names this app expects every tenant to have — declared in the
|
|
55
|
-
# initializer (`
|
|
56
|
-
# whatever seeds memberships. A registry, not a mechanism:
|
|
55
|
+
# initializer (`OpenLoam.default_roles = %w[manager employee]`) and read by
|
|
56
|
+
# whatever seeds memberships. A registry, not a mechanism: OpenLoam does not
|
|
57
57
|
# create roles for you, because who gets which role is business logic.
|
|
58
58
|
def self.default_roles
|
|
59
59
|
@default_roles ||= []
|
|
@@ -64,7 +64,7 @@ module Loam
|
|
|
64
64
|
end
|
|
65
65
|
|
|
66
66
|
# App-wide setting defaults: `{ "billing.currency" => "USD" }`, declared in
|
|
67
|
-
# the initializer and read by
|
|
67
|
+
# the initializer and read by OpenLoam::Configs as the baseline a key resolves
|
|
68
68
|
# to when no global row and no tenant override exist. A registry, like
|
|
69
69
|
# default_roles — declaring a default here needs no migration and no row.
|
|
70
70
|
def self.config_defaults
|
|
@@ -75,8 +75,8 @@ module Loam
|
|
|
75
75
|
@config_defaults = defaults.to_h.transform_keys(&:to_s)
|
|
76
76
|
end
|
|
77
77
|
|
|
78
|
-
# Event-name patterns (
|
|
79
|
-
# pushed to the browser over SSE (
|
|
78
|
+
# Event-name patterns (OpenLoam::Events.pattern_matches?) whose events may be
|
|
79
|
+
# pushed to the browser over SSE (OpenLoam::EventStream). DEFAULT OFF — an empty
|
|
80
80
|
# list means nothing reaches a browser; an app opts in explicitly, so a stray
|
|
81
81
|
# event never leaks by default.
|
|
82
82
|
def self.broadcast_events
|
|
@@ -87,8 +87,8 @@ module Loam
|
|
|
87
87
|
@broadcast_events = Array(patterns).map(&:to_s)
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
-
# The locales content translations (
|
|
91
|
-
# declared in the initializer (`
|
|
90
|
+
# The locales content translations (OpenLoam::Translatable) may be authored in —
|
|
91
|
+
# declared in the initializer (`OpenLoam.locales = %w[en de pl]`), so the admin
|
|
92
92
|
# knows which languages to offer. A registry like the others; defaults to the
|
|
93
93
|
# single default locale.
|
|
94
94
|
def self.locales
|
|
@@ -100,9 +100,9 @@ module Loam
|
|
|
100
100
|
end
|
|
101
101
|
|
|
102
102
|
# Job classes an app EXPLICITLY allows the scheduler to run, beyond the ones
|
|
103
|
-
# it registers via
|
|
103
|
+
# it registers via OpenLoam::Scheduler.register. An allowlist (not "any
|
|
104
104
|
# ActiveJob") so a tenant admin can't schedule ActiveStorage::PurgeJob or a
|
|
105
|
-
# mailer. Declared in the initializer: `
|
|
105
|
+
# mailer. Declared in the initializer: `OpenLoam.schedulable_jobs = %w[DigestJob]`.
|
|
106
106
|
def self.schedulable_jobs
|
|
107
107
|
@schedulable_jobs ||= []
|
|
108
108
|
end
|
|
@@ -116,17 +116,17 @@ module Loam
|
|
|
116
116
|
end
|
|
117
117
|
|
|
118
118
|
# The current request/job locale — content reads overlay onto it. Request
|
|
119
|
-
# state like the tenant (set in a before_action, reset with
|
|
119
|
+
# state like the tenant (set in a before_action, reset with OpenLoam::Current).
|
|
120
120
|
def self.locale
|
|
121
|
-
(
|
|
121
|
+
(OpenLoam::Current.locale || default_locale).to_s
|
|
122
122
|
end
|
|
123
123
|
|
|
124
124
|
def self.locale=(code)
|
|
125
|
-
|
|
125
|
+
OpenLoam::Current.locale = code&.to_s
|
|
126
126
|
end
|
|
127
127
|
|
|
128
128
|
# Known feature flags: `{ "beta_dashboard" => { default: false, description:
|
|
129
|
-
# "..." } }`, declared in the initializer and read by
|
|
129
|
+
# "..." } }`, declared in the initializer and read by OpenLoam::Features. A
|
|
130
130
|
# registry like the others — a flag with no row resolves to its declared
|
|
131
131
|
# default, and the admin can list EVERY known flag, not just toggled ones.
|
|
132
132
|
def self.feature_defaults
|
|
@@ -143,8 +143,8 @@ module Loam
|
|
|
143
143
|
end
|
|
144
144
|
end
|
|
145
145
|
|
|
146
|
-
# The public surface is on
|
|
147
|
-
# `
|
|
146
|
+
# The public surface is on OpenLoam itself — apps and agents write
|
|
147
|
+
# `OpenLoam.on_tenant_created`, never `OpenLoam::Lifecycle.on_tenant_created`.
|
|
148
148
|
def self.on_tenant_created(&block) = Lifecycle.on_tenant_created(&block)
|
|
149
149
|
def self.tenant_created_callbacks = Lifecycle.tenant_created_callbacks
|
|
150
150
|
def self.sync_tenants! = Lifecycle.sync_tenants!
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
#
|
|
2
|
-
# packages it, and added to the app's I18n load path by
|
|
3
|
-
# localizes
|
|
4
|
-
# these keys — distinct from
|
|
1
|
+
# OpenLoam's own UI strings (the framework chrome). Shipped inside lib/ so the gem
|
|
2
|
+
# packages it, and added to the app's I18n load path by OpenLoam::Engine. An app
|
|
3
|
+
# localizes OpenLoam by adding its own `config/locales/open_loam.<locale>.yml` overriding
|
|
4
|
+
# these keys — distinct from OpenLoam::Translatable, which localizes record DATA.
|
|
5
5
|
# Entity and field names come from Rails' own activerecord.models /
|
|
6
6
|
# activerecord.attributes (via model_name.human / human_attribute_name).
|
|
7
7
|
en:
|
|
8
|
-
|
|
8
|
+
open_loam:
|
|
9
9
|
chrome:
|
|
10
10
|
tenant: "Tenant"
|
|
11
11
|
user: "User"
|
|
@@ -1,22 +1,22 @@
|
|
|
1
1
|
require "json"
|
|
2
2
|
|
|
3
|
-
module
|
|
3
|
+
module OpenLoam
|
|
4
4
|
module Mcp
|
|
5
|
-
# The stdio transport for the
|
|
5
|
+
# The stdio transport for the OpenLoam MCP server (L-302): newline-delimited
|
|
6
6
|
# JSON-RPC over stdin/stdout, the MCP-spec stdio binding. Authenticates ONCE
|
|
7
|
-
# from
|
|
7
|
+
# from OPEN_LOAM_MCP_TOKEN (a OpenLoam API token — never a tool argument, so it can't
|
|
8
8
|
# land in an agent transcript), then runs every message inside that token's
|
|
9
9
|
# tenant + actor. Nothing but JSON-RPC is written to stdout; logs go to stderr
|
|
10
10
|
# (wired in the rake task) — a stray stdout line corrupts the stream.
|
|
11
11
|
class Server
|
|
12
|
-
def initialize(input: $stdin, output: $stdout, token: ENV["
|
|
12
|
+
def initialize(input: $stdin, output: $stdout, token: ENV["OPEN_LOAM_MCP_TOKEN"])
|
|
13
13
|
@input = input
|
|
14
14
|
@output = output
|
|
15
15
|
@token = token
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def run
|
|
19
|
-
api_token =
|
|
19
|
+
api_token = OpenLoam::ApiToken.authenticate(@token)
|
|
20
20
|
return fail_auth unless api_token
|
|
21
21
|
|
|
22
22
|
tenant = api_token.tenant
|
|
@@ -27,13 +27,13 @@ module Loam
|
|
|
27
27
|
next if line.empty?
|
|
28
28
|
|
|
29
29
|
request = parse(line)
|
|
30
|
-
next write(
|
|
30
|
+
next write(OpenLoam::Mcp.err(nil, -32700, "parse error")) if request.nil?
|
|
31
31
|
|
|
32
32
|
begin
|
|
33
|
-
response =
|
|
33
|
+
response = OpenLoam.as_tenant(tenant, actor: actor) { OpenLoam::Mcp.handle_jsonrpc(request) }
|
|
34
34
|
write(response) if response
|
|
35
35
|
ensure
|
|
36
|
-
|
|
36
|
+
OpenLoam::Current.reset
|
|
37
37
|
end
|
|
38
38
|
end
|
|
39
39
|
end
|
|
@@ -52,7 +52,7 @@ module Loam
|
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
def fail_auth
|
|
55
|
-
write(
|
|
55
|
+
write(OpenLoam::Mcp.err(nil, -32001, "authentication failed: set OPEN_LOAM_MCP_TOKEN to a valid OpenLoam API token"))
|
|
56
56
|
exit(1)
|
|
57
57
|
end
|
|
58
58
|
end
|