alloy-runtime-cli 0.1.0__py3-none-any.whl
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.
- alloy_runtime_cli-0.1.0.dist-info/METADATA +61 -0
- alloy_runtime_cli-0.1.0.dist-info/RECORD +451 -0
- alloy_runtime_cli-0.1.0.dist-info/WHEEL +5 -0
- alloy_runtime_cli-0.1.0.dist-info/entry_points.txt +2 -0
- alloy_runtime_cli-0.1.0.dist-info/top_level.txt +1 -0
- cli/__init__.py +0 -0
- cli/commands/__init__.py +0 -0
- cli/commands/admin/__init__.py +0 -0
- cli/commands/admin/bootstrap_command.py +118 -0
- cli/commands/admin/credentials/__init__.py +0 -0
- cli/commands/admin/credentials/create/__init__.py +0 -0
- cli/commands/admin/credentials/create/command.py +148 -0
- cli/commands/admin/credentials/create/presenter.py +16 -0
- cli/commands/admin/credentials/grant/__init__.py +0 -0
- cli/commands/admin/credentials/grant/command.py +119 -0
- cli/commands/admin/credentials/grant/fields.py +33 -0
- cli/commands/admin/credentials/grant/presenter.py +23 -0
- cli/commands/agents/__init__.py +0 -0
- cli/commands/agents/create/__init__.py +0 -0
- cli/commands/agents/create/command.py +475 -0
- cli/commands/agents/create/fields.py +64 -0
- cli/commands/agents/create/presenter.py +68 -0
- cli/commands/agents/delete/__init__.py +0 -0
- cli/commands/agents/delete/command.py +47 -0
- cli/commands/agents/delete/presenter.py +16 -0
- cli/commands/agents/get/command.py +37 -0
- cli/commands/agents/get/presenter.py +32 -0
- cli/commands/agents/list/__init__.py +1 -0
- cli/commands/agents/list/command.py +54 -0
- cli/commands/agents/list/presenter.py +82 -0
- cli/commands/agents/update/__init__.py +0 -0
- cli/commands/agents/update/command.py +435 -0
- cli/commands/agents/update/fields.py +40 -0
- cli/commands/agents/update/presenter.py +68 -0
- cli/commands/audio/__init__.py +0 -0
- cli/commands/audio/transcribe/__init__.py +0 -0
- cli/commands/audio/transcribe/command.py +144 -0
- cli/commands/audio/transcribe/presenter.py +15 -0
- cli/commands/auth/__init__.py +0 -0
- cli/commands/auth/login/__init__.py +0 -0
- cli/commands/auth/login/command.py +80 -0
- cli/commands/auth/signup/__init__.py +0 -0
- cli/commands/auth/signup/command.py +115 -0
- cli/commands/billing/__init__.py +1 -0
- cli/commands/billing/costs/__init__.py +1 -0
- cli/commands/billing/costs/by_agent/__init__.py +1 -0
- cli/commands/billing/costs/by_agent/command.py +57 -0
- cli/commands/billing/costs/by_agent/presenter.py +81 -0
- cli/commands/billing/costs/by_model/__init__.py +1 -0
- cli/commands/billing/costs/by_model/command.py +57 -0
- cli/commands/billing/costs/by_model/presenter.py +80 -0
- cli/commands/billing/costs/daily/__init__.py +1 -0
- cli/commands/billing/costs/daily/command.py +55 -0
- cli/commands/billing/costs/daily/presenter.py +75 -0
- cli/commands/billing/costs/summary/__init__.py +1 -0
- cli/commands/billing/costs/summary/command.py +57 -0
- cli/commands/billing/costs/summary/presenter.py +42 -0
- cli/commands/billing/projects/__init__.py +1 -0
- cli/commands/billing/projects/create/__init__.py +1 -0
- cli/commands/billing/projects/create/command.py +60 -0
- cli/commands/billing/projects/create/presenter.py +26 -0
- cli/commands/billing/projects/get/__init__.py +1 -0
- cli/commands/billing/projects/get/command.py +33 -0
- cli/commands/billing/projects/get/presenter.py +32 -0
- cli/commands/billing/projects/list/__init__.py +1 -0
- cli/commands/billing/projects/list/command.py +40 -0
- cli/commands/billing/projects/list/presenter.py +57 -0
- cli/commands/content/__init__.py +1 -0
- cli/commands/content/delete/__init__.py +0 -0
- cli/commands/content/delete/command.py +49 -0
- cli/commands/content/delete/presenter.py +18 -0
- cli/commands/content/edit/__init__.py +1 -0
- cli/commands/content/edit/command.py +155 -0
- cli/commands/content/edit/editor.py +150 -0
- cli/commands/content/edit/presenter.py +146 -0
- cli/commands/content/get/__init__.py +1 -0
- cli/commands/content/get/command.py +39 -0
- cli/commands/content/get/presenter.py +176 -0
- cli/commands/content/list/__init__.py +1 -0
- cli/commands/content/list/command.py +347 -0
- cli/commands/content/list/export_formatters.py +409 -0
- cli/commands/content/list/export_handler.py +165 -0
- cli/commands/content/list/presenter.py +190 -0
- cli/commands/credentials/__init__.py +0 -0
- cli/commands/credentials/create/__init__.py +0 -0
- cli/commands/credentials/create/command.py +165 -0
- cli/commands/credentials/create/fields.py +38 -0
- cli/commands/credentials/create/presenter.py +20 -0
- cli/commands/credentials/update/__init__.py +0 -0
- cli/commands/credentials/update/command.py +53 -0
- cli/commands/credentials/update/fields.py +71 -0
- cli/commands/credentials/update/presenter.py +16 -0
- cli/commands/flag_utils.py +366 -0
- cli/commands/generate/__init__.py +0 -0
- cli/commands/generate/cancel/__init__.py +1 -0
- cli/commands/generate/cancel/command.py +44 -0
- cli/commands/generate/cancel/presenter.py +26 -0
- cli/commands/generate/status/__init__.py +1 -0
- cli/commands/generate/status/command.py +58 -0
- cli/commands/generate/status/presenter.py +78 -0
- cli/commands/generate/text/__init__.py +0 -0
- cli/commands/generate/text/command.py +1325 -0
- cli/commands/generate/text/concurrent_renderer.py +355 -0
- cli/commands/generate/text/presenter.py +287 -0
- cli/commands/generate/text/stream_renderer.py +129 -0
- cli/commands/knowledge/__init__.py +0 -0
- cli/commands/knowledge/collections/__init__.py +0 -0
- cli/commands/knowledge/collections/cluster/__init__.py +0 -0
- cli/commands/knowledge/collections/cluster/command.py +64 -0
- cli/commands/knowledge/collections/cluster/presenter.py +74 -0
- cli/commands/knowledge/collections/cluster_status/__init__.py +0 -0
- cli/commands/knowledge/collections/cluster_status/command.py +46 -0
- cli/commands/knowledge/collections/cluster_status/presenter.py +10 -0
- cli/commands/knowledge/collections/create/__init__.py +0 -0
- cli/commands/knowledge/collections/create/command.py +137 -0
- cli/commands/knowledge/collections/create/presenter.py +38 -0
- cli/commands/knowledge/collections/delete/__init__.py +1 -0
- cli/commands/knowledge/collections/delete/command.py +47 -0
- cli/commands/knowledge/collections/delete/presenter.py +20 -0
- cli/commands/knowledge/collections/get/__init__.py +1 -0
- cli/commands/knowledge/collections/get/command.py +30 -0
- cli/commands/knowledge/collections/get/presenter.py +44 -0
- cli/commands/knowledge/collections/list/__init__.py +1 -0
- cli/commands/knowledge/collections/list/command.py +41 -0
- cli/commands/knowledge/collections/list/presenter.py +68 -0
- cli/commands/knowledge/collections/update/__init__.py +0 -0
- cli/commands/knowledge/collections/update/command.py +97 -0
- cli/commands/knowledge/collections/update/presenter.py +42 -0
- cli/commands/knowledge/documents/__init__.py +0 -0
- cli/commands/knowledge/documents/bulk_metadata/__init__.py +0 -0
- cli/commands/knowledge/documents/bulk_metadata/command.py +119 -0
- cli/commands/knowledge/documents/bulk_metadata/presenter.py +36 -0
- cli/commands/knowledge/documents/delete/__init__.py +0 -0
- cli/commands/knowledge/documents/delete/command.py +47 -0
- cli/commands/knowledge/documents/delete/presenter.py +20 -0
- cli/commands/knowledge/documents/get/__init__.py +0 -0
- cli/commands/knowledge/documents/get/command.py +39 -0
- cli/commands/knowledge/documents/get/presenter.py +78 -0
- cli/commands/knowledge/documents/ingest/__init__.py +0 -0
- cli/commands/knowledge/documents/ingest/command.py +222 -0
- cli/commands/knowledge/documents/ingest/presenter.py +41 -0
- cli/commands/knowledge/documents/list/__init__.py +0 -0
- cli/commands/knowledge/documents/list/command.py +69 -0
- cli/commands/knowledge/documents/list/presenter.py +86 -0
- cli/commands/knowledge/documents/reingest/__init__.py +0 -0
- cli/commands/knowledge/documents/reingest/command.py +102 -0
- cli/commands/knowledge/documents/reingest/presenter.py +70 -0
- cli/commands/knowledge/documents/update/__init__.py +0 -0
- cli/commands/knowledge/documents/update/command.py +85 -0
- cli/commands/knowledge/documents/update/presenter.py +37 -0
- cli/commands/knowledge/recover/__init__.py +0 -0
- cli/commands/knowledge/recover/command.py +46 -0
- cli/commands/knowledge/recover/presenter.py +79 -0
- cli/commands/knowledge/search/__init__.py +0 -0
- cli/commands/knowledge/search/command.py +218 -0
- cli/commands/knowledge/search/presenter.py +111 -0
- cli/commands/knowledge/synthesis/__init__.py +0 -0
- cli/commands/knowledge/synthesis/create/__init__.py +0 -0
- cli/commands/knowledge/synthesis/create/command.py +127 -0
- cli/commands/knowledge/synthesis/create/presenter.py +33 -0
- cli/commands/knowledge/synthesis/delete/__init__.py +0 -0
- cli/commands/knowledge/synthesis/delete/command.py +53 -0
- cli/commands/knowledge/synthesis/delete/presenter.py +31 -0
- cli/commands/knowledge/synthesis/get/__init__.py +0 -0
- cli/commands/knowledge/synthesis/get/command.py +55 -0
- cli/commands/knowledge/synthesis/get/presenter.py +114 -0
- cli/commands/knowledge/synthesis/list/__init__.py +0 -0
- cli/commands/knowledge/synthesis/list/command.py +132 -0
- cli/commands/knowledge/synthesis/list/presenter.py +84 -0
- cli/commands/knowledge/synthesis/refresh/__init__.py +0 -0
- cli/commands/knowledge/synthesis/refresh/command.py +42 -0
- cli/commands/knowledge/synthesis/refresh/presenter.py +33 -0
- cli/commands/knowledge/synthesis/update/__init__.py +0 -0
- cli/commands/knowledge/synthesis/update/command.py +76 -0
- cli/commands/knowledge/synthesis/update/presenter.py +41 -0
- cli/commands/models/__init__.py +0 -0
- cli/commands/models/list/__init__.py +0 -0
- cli/commands/models/list/command.py +84 -0
- cli/commands/models/list/presenter.py +114 -0
- cli/commands/organizations/__init__.py +0 -0
- cli/commands/organizations/create/command.py +32 -0
- cli/commands/organizations/create/presenter.py +9 -0
- cli/commands/pipelines/__init__.py +1 -0
- cli/commands/pipelines/approvals/__init__.py +1 -0
- cli/commands/pipelines/approvals/decide_command.py +77 -0
- cli/commands/pipelines/approvals/get_command.py +44 -0
- cli/commands/pipelines/approvals/presenter.py +56 -0
- cli/commands/pipelines/costs/__init__.py +1 -0
- cli/commands/pipelines/costs/command.py +57 -0
- cli/commands/pipelines/costs/daily_command.py +54 -0
- cli/commands/pipelines/costs/daily_presenter.py +59 -0
- cli/commands/pipelines/costs/presenter.py +37 -0
- cli/commands/pipelines/create/__init__.py +1 -0
- cli/commands/pipelines/create/command.py +103 -0
- cli/commands/pipelines/create/presenter.py +22 -0
- cli/commands/pipelines/env_vars/__init__.py +1 -0
- cli/commands/pipelines/env_vars/command.py +51 -0
- cli/commands/pipelines/env_vars/presenter.py +16 -0
- cli/commands/pipelines/execute/__init__.py +1 -0
- cli/commands/pipelines/execute/command.py +142 -0
- cli/commands/pipelines/execute/presenter.py +47 -0
- cli/commands/pipelines/executions/__init__.py +1 -0
- cli/commands/pipelines/executions/costs/__init__.py +1 -0
- cli/commands/pipelines/executions/costs/command.py +48 -0
- cli/commands/pipelines/executions/costs/presenter.py +29 -0
- cli/commands/pipelines/executions/costs_by_model/__init__.py +1 -0
- cli/commands/pipelines/executions/costs_by_model/command.py +50 -0
- cli/commands/pipelines/executions/costs_by_model/presenter.py +78 -0
- cli/commands/pipelines/executions/costs_by_step/__init__.py +1 -0
- cli/commands/pipelines/executions/costs_by_step/command.py +50 -0
- cli/commands/pipelines/executions/costs_by_step/presenter.py +72 -0
- cli/commands/pipelines/executions/get_command.py +38 -0
- cli/commands/pipelines/executions/list_command.py +123 -0
- cli/commands/pipelines/executions/presenter.py +131 -0
- cli/commands/pipelines/executions/rerun_command.py +41 -0
- cli/commands/pipelines/executions/update/__init__.py +1 -0
- cli/commands/pipelines/executions/update/command.py +110 -0
- cli/commands/pipelines/executions/update/presenter.py +28 -0
- cli/commands/pipelines/get/__init__.py +1 -0
- cli/commands/pipelines/get/command.py +33 -0
- cli/commands/pipelines/get/presenter.py +48 -0
- cli/commands/pipelines/list/__init__.py +1 -0
- cli/commands/pipelines/list/command.py +53 -0
- cli/commands/pipelines/list/presenter.py +66 -0
- cli/commands/pipelines/schedules/__init__.py +1 -0
- cli/commands/pipelines/schedules/create_command.py +119 -0
- cli/commands/pipelines/schedules/create_presenter.py +35 -0
- cli/commands/pipelines/schedules/delete_command.py +52 -0
- cli/commands/pipelines/schedules/env_vars_command.py +59 -0
- cli/commands/pipelines/schedules/env_vars_presenter.py +16 -0
- cli/commands/pipelines/schedules/get_command.py +38 -0
- cli/commands/pipelines/schedules/list_command.py +33 -0
- cli/commands/pipelines/schedules/once_command.py +90 -0
- cli/commands/pipelines/schedules/once_presenter.py +30 -0
- cli/commands/pipelines/schedules/presenter.py +104 -0
- cli/commands/pipelines/schedules/update_command.py +139 -0
- cli/commands/pipelines/schedules/update_presenter.py +29 -0
- cli/commands/render/__init__.py +0 -0
- cli/commands/render/html_to_image/__init__.py +0 -0
- cli/commands/render/html_to_image/command.py +170 -0
- cli/commands/schemas/__init__.py +0 -0
- cli/commands/schemas/create/__init__.py +0 -0
- cli/commands/schemas/create/command.py +122 -0
- cli/commands/schemas/create/presenter.py +53 -0
- cli/commands/schemas/delete/command.py +45 -0
- cli/commands/schemas/delete/presenter.py +9 -0
- cli/commands/schemas/get/__init__.py +0 -0
- cli/commands/schemas/get/command.py +56 -0
- cli/commands/schemas/get/presenter.py +129 -0
- cli/commands/schemas/list/__init__.py +0 -0
- cli/commands/schemas/list/command.py +64 -0
- cli/commands/schemas/list/presenter.py +133 -0
- cli/commands/schemas/update/__init__.py +0 -0
- cli/commands/schemas/update/command.py +369 -0
- cli/commands/schemas/update/presenter.py +53 -0
- cli/commands/sessions/__init__.py +1 -0
- cli/commands/sessions/delete/__init__.py +1 -0
- cli/commands/sessions/delete/command.py +47 -0
- cli/commands/sessions/delete/presenter.py +10 -0
- cli/commands/sessions/get/__init__.py +1 -0
- cli/commands/sessions/get/command.py +42 -0
- cli/commands/sessions/get/presenter.py +59 -0
- cli/commands/sessions/list/__init__.py +1 -0
- cli/commands/sessions/list/command.py +61 -0
- cli/commands/sessions/list/presenter.py +68 -0
- cli/commands/sessions/messages/__init__.py +1 -0
- cli/commands/sessions/messages/command.py +78 -0
- cli/commands/sessions/messages/presenter.py +79 -0
- cli/commands/shared_flags.py +500 -0
- cli/commands/sync/__init__.py +0 -0
- cli/commands/sync/command.py +45 -0
- cli/commands/sync/presenter.py +49 -0
- cli/commands/tags/__init__.py +1 -0
- cli/commands/tags/create/__init__.py +1 -0
- cli/commands/tags/create/command.py +60 -0
- cli/commands/tags/delete/__init__.py +1 -0
- cli/commands/tags/delete/command.py +47 -0
- cli/commands/tags/delete/presenter.py +10 -0
- cli/commands/tags/get/command.py +31 -0
- cli/commands/tags/get/presenter.py +23 -0
- cli/commands/tags/list/__init__.py +1 -0
- cli/commands/tags/list/command.py +52 -0
- cli/commands/tags/list/presenter.py +49 -0
- cli/commands/tags/update/command.py +64 -0
- cli/commands/tags/update/presenter.py +9 -0
- cli/commands/templates/__init__.py +0 -0
- cli/commands/templates/create/__init__.py +0 -0
- cli/commands/templates/create/command.py +152 -0
- cli/commands/templates/create/presenter.py +86 -0
- cli/commands/templates/delete/__init__.py +0 -0
- cli/commands/templates/delete/command.py +47 -0
- cli/commands/templates/delete/presenter.py +16 -0
- cli/commands/templates/get/__init__.py +0 -0
- cli/commands/templates/get/command.py +52 -0
- cli/commands/templates/get/presenter.py +233 -0
- cli/commands/templates/get_by_version/command.py +32 -0
- cli/commands/templates/get_by_version/presenter.py +30 -0
- cli/commands/templates/list/__init__.py +1 -0
- cli/commands/templates/list/command.py +102 -0
- cli/commands/templates/list/presenter.py +93 -0
- cli/commands/templates/render/__init__.py +0 -0
- cli/commands/templates/render/command.py +115 -0
- cli/commands/templates/render/presenter.py +276 -0
- cli/commands/templates/update/__init__.py +0 -0
- cli/commands/templates/update/command.py +199 -0
- cli/commands/templates/update/presenter.py +94 -0
- cli/commands/templates/version/__init__.py +1 -0
- cli/commands/templates/version/command.py +116 -0
- cli/commands/templates/version/presenter.py +100 -0
- cli/commands/tool_configs/__init__.py +0 -0
- cli/commands/tool_configs/create/__init__.py +0 -0
- cli/commands/tool_configs/create/command.py +118 -0
- cli/commands/tool_configs/create/presenter.py +53 -0
- cli/commands/tool_configs/delete/__init__.py +0 -0
- cli/commands/tool_configs/delete/command.py +47 -0
- cli/commands/tool_configs/delete/presenter.py +18 -0
- cli/commands/tool_configs/get/__init__.py +0 -0
- cli/commands/tool_configs/get/command.py +31 -0
- cli/commands/tool_configs/get/presenter.py +62 -0
- cli/commands/tool_configs/list/__init__.py +0 -0
- cli/commands/tool_configs/list/command.py +59 -0
- cli/commands/tool_configs/list/presenter.py +60 -0
- cli/commands/tool_configs/update/__init__.py +0 -0
- cli/commands/tool_configs/update/command.py +128 -0
- cli/commands/tool_configs/update/presenter.py +53 -0
- cli/commands/tools/__init__.py +1 -0
- cli/commands/tools/get/__init__.py +1 -0
- cli/commands/tools/get/command.py +42 -0
- cli/commands/tools/get/presenter.py +45 -0
- cli/commands/tools/list/__init__.py +1 -0
- cli/commands/tools/list/command.py +56 -0
- cli/commands/tools/list/presenter.py +44 -0
- cli/commands/users/__init__.py +0 -0
- cli/commands/users/create/command.py +53 -0
- cli/commands/users/create/presenter.py +9 -0
- cli/commands/whoami/__init__.py +0 -0
- cli/commands/whoami/command.py +42 -0
- cli/infrastructure/__init__.py +0 -0
- cli/infrastructure/auth_storage.py +71 -0
- cli/infrastructure/client_factory.py +36 -0
- cli/infrastructure/command.py +75 -0
- cli/infrastructure/config.py +188 -0
- cli/infrastructure/console.py +27 -0
- cli/infrastructure/editor.py +138 -0
- cli/infrastructure/error_display.py +178 -0
- cli/infrastructure/field_extractor.py +360 -0
- cli/infrastructure/file_content.py +210 -0
- cli/infrastructure/filter_parser.py +256 -0
- cli/infrastructure/formatters/__init__.py +0 -0
- cli/infrastructure/formatters/base.py +99 -0
- cli/infrastructure/formatters/compact_formatter.py +245 -0
- cli/infrastructure/formatters/json_formatter.py +84 -0
- cli/infrastructure/formatters/lines_formatter.py +102 -0
- cli/infrastructure/formatting/__init__.py +0 -0
- cli/infrastructure/formatting/fields.py +193 -0
- cli/infrastructure/forms/__init__.py +0 -0
- cli/infrastructure/forms/agent_picker.py +123 -0
- cli/infrastructure/forms/agent_tool_editor.py +384 -0
- cli/infrastructure/forms/agent_tools_manager.py +212 -0
- cli/infrastructure/forms/base_picker.py +469 -0
- cli/infrastructure/forms/components.py +126 -0
- cli/infrastructure/forms/json_schema_builder.py +149 -0
- cli/infrastructure/forms/model_picker.py +134 -0
- cli/infrastructure/forms/parsers.py +173 -0
- cli/infrastructure/forms/resolution_modal.py +302 -0
- cli/infrastructure/forms/schema_picker.py +137 -0
- cli/infrastructure/forms/tag_management_modal.py +103 -0
- cli/infrastructure/forms/tag_picker.py +207 -0
- cli/infrastructure/forms/template_picker.py +131 -0
- cli/infrastructure/forms/tool_config_picker.py +130 -0
- cli/infrastructure/forms/tool_picker.py +103 -0
- cli/infrastructure/injection/__init__.py +0 -0
- cli/infrastructure/injection/parser.py +302 -0
- cli/infrastructure/injection/resolver.py +399 -0
- cli/infrastructure/kv_parser.py +130 -0
- cli/infrastructure/local_storage.py +227 -0
- cli/infrastructure/macro_parser.py +215 -0
- cli/infrastructure/output.py +192 -0
- cli/infrastructure/provider_setup.py +81 -0
- cli/infrastructure/renderers/__init__.py +0 -0
- cli/infrastructure/renderers/entity_renderer.py +77 -0
- cli/infrastructure/renderers/list_renderer.py +114 -0
- cli/infrastructure/scope_utils.py +47 -0
- cli/infrastructure/spinner.py +101 -0
- cli/infrastructure/tui/__init__.py +0 -0
- cli/infrastructure/tui/clipboard.py +41 -0
- cli/infrastructure/tui/formatters.py +105 -0
- cli/infrastructure/tui/preview.py +14 -0
- cli/infrastructure/tui/selectable.py +198 -0
- cli/infrastructure/validation/__init__.py +0 -0
- cli/infrastructure/validation/tag_validation.py +74 -0
- cli/main.py +759 -0
- cli/tui/__init__.py +0 -0
- cli/tui/app.py +199 -0
- cli/tui/app_store.py +73 -0
- cli/tui/chat/__init__.py +0 -0
- cli/tui/chat/commands/__init__.py +0 -0
- cli/tui/chat/commands/base.py +65 -0
- cli/tui/chat/commands/create_session.py +135 -0
- cli/tui/chat/commands/load_session.py +119 -0
- cli/tui/chat/commands/regenerate.py +120 -0
- cli/tui/chat/commands/reload_session.py +63 -0
- cli/tui/chat/commands/send_message.py +190 -0
- cli/tui/chat/commands/undo.py +66 -0
- cli/tui/chat/editor.py +71 -0
- cli/tui/chat/messages.py +223 -0
- cli/tui/chat/pane.py +141 -0
- cli/tui/chat/renderers/__init__.py +0 -0
- cli/tui/chat/renderers/base.py +72 -0
- cli/tui/chat/renderers/markdown.py +250 -0
- cli/tui/chat/renderers/plain.py +83 -0
- cli/tui/chat/screen.py +1155 -0
- cli/tui/chat/services/__init__.py +0 -0
- cli/tui/chat/services/injection.py +386 -0
- cli/tui/chat/services/name_generator.py +256 -0
- cli/tui/chat/slash_commands.py +424 -0
- cli/tui/chat/store.py +280 -0
- cli/tui/chat/types.py +220 -0
- cli/tui/chat/widgets/__init__.py +0 -0
- cli/tui/chat/widgets/chat_header.py +75 -0
- cli/tui/chat/widgets/chat_input.py +362 -0
- cli/tui/chat/widgets/injection_popup.py +161 -0
- cli/tui/chat/widgets/message_display.py +287 -0
- cli/tui/chat/widgets/session_sidebar.py +214 -0
- cli/tui/chat/widgets/welcome_screen.py +290 -0
- cli/tui/screens/__init__.py +0 -0
- cli/tui/screens/agents.py +344 -0
- cli/tui/screens/base.py +301 -0
- cli/tui/screens/content.py +508 -0
- cli/tui/screens/dashboard.py +89 -0
- cli/tui/screens/models.py +96 -0
- cli/tui/screens/nav_screen.py +186 -0
- cli/tui/screens/schemas.py +522 -0
- cli/tui/screens/templates.py +734 -0
- cli/tui/screens/tool_configs.py +335 -0
- cli/tui/styles/__init__.py +0 -0
- cli/tui/widgets/__init__.py +0 -0
- cli/tui/widgets/agent_create_modal.py +139 -0
- cli/tui/widgets/agent_form_modal.py +659 -0
- cli/tui/widgets/agent_update_modal.py +299 -0
- cli/tui/widgets/base_form_modal.py +77 -0
- cli/tui/widgets/confirm_modal.py +75 -0
- cli/tui/widgets/help_modal.py +145 -0
- cli/tui/widgets/new_session_modal.py +328 -0
- cli/tui/widgets/schema_create_modal.py +271 -0
- cli/tui/widgets/schema_update_modal.py +188 -0
- cli/tui/widgets/status_footer.py +147 -0
- cli/tui/widgets/template_create_modal.py +502 -0
- cli/tui/widgets/template_update_modal.py +308 -0
- cli/tui/widgets/tool_config_create_modal.py +216 -0
- cli/tui/widgets/tool_config_update_modal.py +208 -0
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"""HTTPie-style key-value parser for CLI options.
|
|
2
|
+
|
|
3
|
+
Provides parsing for key=value and key:=json syntax, allowing users to
|
|
4
|
+
build JSON objects without writing literal JSON strings.
|
|
5
|
+
|
|
6
|
+
Syntax:
|
|
7
|
+
key=value -> {"key": "value"} (string value)
|
|
8
|
+
key:=value -> {"key": <parsed JSON>} (raw JSON: numbers, bools, arrays, objects)
|
|
9
|
+
|
|
10
|
+
Examples:
|
|
11
|
+
name=John -> {"name": "John"}
|
|
12
|
+
count:=5 -> {"count": 5}
|
|
13
|
+
active:=true -> {"active": true}
|
|
14
|
+
tags:='["a","b"]' -> {"tags": ["a", "b"]}
|
|
15
|
+
url=https://foo.com -> {"url": "https://foo.com"} (= in value is preserved)
|
|
16
|
+
|
|
17
|
+
Usage with Typer:
|
|
18
|
+
from cli.infrastructure.kv_parser import kv_pairs_callback
|
|
19
|
+
|
|
20
|
+
@app.command()
|
|
21
|
+
def my_command(
|
|
22
|
+
variables: Annotated[
|
|
23
|
+
list[str] | None,
|
|
24
|
+
typer.Option(
|
|
25
|
+
"--var", "-v",
|
|
26
|
+
help="Variables in key=value or key:=json format",
|
|
27
|
+
callback=kv_pairs_callback,
|
|
28
|
+
),
|
|
29
|
+
] = None,
|
|
30
|
+
) -> None:
|
|
31
|
+
# variables is now a dict[str, Any]
|
|
32
|
+
...
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
import json
|
|
36
|
+
from typing import Any
|
|
37
|
+
|
|
38
|
+
import typer
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def parse_kv_pairs(values: list[str] | None) -> dict[str, Any]:
|
|
42
|
+
"""Parse a list of key=value or key:=json strings into a dictionary.
|
|
43
|
+
|
|
44
|
+
Args:
|
|
45
|
+
values: List of strings in "key=value" or "key:=json" format.
|
|
46
|
+
Can be None or empty.
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
Dictionary with parsed key-value pairs. Empty dict if input is None/empty.
|
|
50
|
+
|
|
51
|
+
Raises:
|
|
52
|
+
typer.BadParameter: If a value has invalid format or invalid JSON for :=
|
|
53
|
+
|
|
54
|
+
Examples:
|
|
55
|
+
>>> parse_kv_pairs(["name=John", "age:=30"])
|
|
56
|
+
{"name": "John", "age": 30}
|
|
57
|
+
|
|
58
|
+
>>> parse_kv_pairs(["active:=true", "tags:=['a','b']"])
|
|
59
|
+
{"active": True, "tags": ["a", "b"]}
|
|
60
|
+
|
|
61
|
+
>>> parse_kv_pairs(None)
|
|
62
|
+
{}
|
|
63
|
+
"""
|
|
64
|
+
if not values:
|
|
65
|
+
return {}
|
|
66
|
+
|
|
67
|
+
result: dict[str, Any] = {}
|
|
68
|
+
|
|
69
|
+
for item in values:
|
|
70
|
+
# Check for raw JSON syntax first (`:=`)
|
|
71
|
+
if ":=" in item:
|
|
72
|
+
key, raw_value = item.split(":=", 1)
|
|
73
|
+
key = key.strip()
|
|
74
|
+
|
|
75
|
+
if not key:
|
|
76
|
+
raise typer.BadParameter(
|
|
77
|
+
f"Missing key in '{item}'. Expected format: key:=json_value"
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
try:
|
|
81
|
+
result[key] = json.loads(raw_value)
|
|
82
|
+
except json.JSONDecodeError as e:
|
|
83
|
+
raise typer.BadParameter(
|
|
84
|
+
f"Invalid JSON for key '{key}': {raw_value}. Error: {e}"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
# Check for string syntax (`=`)
|
|
88
|
+
elif "=" in item:
|
|
89
|
+
key, value = item.split("=", 1)
|
|
90
|
+
key = key.strip()
|
|
91
|
+
|
|
92
|
+
if not key:
|
|
93
|
+
raise typer.BadParameter(
|
|
94
|
+
f"Missing key in '{item}'. Expected format: key=value"
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
result[key] = value
|
|
98
|
+
|
|
99
|
+
else:
|
|
100
|
+
raise typer.BadParameter(
|
|
101
|
+
f"Invalid format: '{item}'. Expected 'key=value' or 'key:=json_value'"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
return result
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def kv_pairs_callback(values: list[str] | None) -> dict[str, Any]:
|
|
108
|
+
"""Typer callback that transforms key-value pairs into a dictionary.
|
|
109
|
+
|
|
110
|
+
Use this as a callback for typer.Option to accept httpie-style key-value
|
|
111
|
+
pairs that get parsed into a dictionary before reaching your command function.
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
values: List of strings from repeated option usage (e.g., -v a=1 -v b=2)
|
|
115
|
+
|
|
116
|
+
Returns:
|
|
117
|
+
Parsed dictionary
|
|
118
|
+
|
|
119
|
+
Example:
|
|
120
|
+
@app.command()
|
|
121
|
+
def cmd(
|
|
122
|
+
vars: Annotated[
|
|
123
|
+
list[str] | None,
|
|
124
|
+
typer.Option("--var", "-v", callback=kv_pairs_callback),
|
|
125
|
+
] = None,
|
|
126
|
+
):
|
|
127
|
+
# vars is a dict here, not list[str]
|
|
128
|
+
print(vars) # {"a": "1", "b": 2}
|
|
129
|
+
"""
|
|
130
|
+
return parse_kv_pairs(values)
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"""Local JSON storage for CLI persistent data.
|
|
2
|
+
|
|
3
|
+
Provides a scalable storage system for various CLI features like command history,
|
|
4
|
+
preferences, cached data, etc. Data is stored in ~/.alloy-runtime/storage.json.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
from typing import Any, cast
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
CONFIG_DIR = Path.home() / ".alloy-runtime"
|
|
14
|
+
STORAGE_FILE = CONFIG_DIR / "storage.json"
|
|
15
|
+
|
|
16
|
+
# Default limits for various storage features
|
|
17
|
+
MAX_COMMAND_HISTORY = 15
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class LocalStorage:
|
|
21
|
+
"""JSON-based local storage for CLI persistent data.
|
|
22
|
+
|
|
23
|
+
Provides getter/setter methods for various storage features.
|
|
24
|
+
Data is automatically persisted to ~/.alloy-runtime/storage.json.
|
|
25
|
+
|
|
26
|
+
Example:
|
|
27
|
+
storage = LocalStorage()
|
|
28
|
+
|
|
29
|
+
# Command history
|
|
30
|
+
storage.add_command("Hello, how are you?")
|
|
31
|
+
history = storage.get_command_history()
|
|
32
|
+
|
|
33
|
+
# Future features can add their own sections
|
|
34
|
+
storage.set("preferences", "theme", "dark")
|
|
35
|
+
theme = storage.get("preferences", "theme", default="light")
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
def __init__(self) -> None:
|
|
39
|
+
self._data: dict[str, Any] = self._load()
|
|
40
|
+
|
|
41
|
+
def _load(self) -> dict[str, Any]:
|
|
42
|
+
"""Load storage data from disk."""
|
|
43
|
+
if not STORAGE_FILE.exists():
|
|
44
|
+
return self._default_data()
|
|
45
|
+
|
|
46
|
+
try:
|
|
47
|
+
with open(STORAGE_FILE, encoding="utf-8") as f:
|
|
48
|
+
data = json.load(f)
|
|
49
|
+
# Ensure all expected keys exist
|
|
50
|
+
return self._merge_with_defaults(data)
|
|
51
|
+
except (json.JSONDecodeError, OSError):
|
|
52
|
+
# If file is corrupted, start fresh
|
|
53
|
+
return self._default_data()
|
|
54
|
+
|
|
55
|
+
def _default_data(self) -> dict[str, Any]:
|
|
56
|
+
"""Return default storage structure."""
|
|
57
|
+
return {
|
|
58
|
+
"command_history": [],
|
|
59
|
+
"preferences": {},
|
|
60
|
+
"cache": {},
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
def _merge_with_defaults(self, data: dict[str, Any]) -> dict[str, Any]:
|
|
64
|
+
"""Merge loaded data with defaults to ensure all keys exist."""
|
|
65
|
+
defaults = self._default_data()
|
|
66
|
+
for key, value in defaults.items():
|
|
67
|
+
if key not in data:
|
|
68
|
+
data[key] = value
|
|
69
|
+
return data
|
|
70
|
+
|
|
71
|
+
def _save(self) -> None:
|
|
72
|
+
"""Save storage data to disk."""
|
|
73
|
+
CONFIG_DIR.mkdir(exist_ok=True, mode=0o700)
|
|
74
|
+
|
|
75
|
+
with open(STORAGE_FILE, "w", encoding="utf-8") as f:
|
|
76
|
+
json.dump(self._data, f, indent=2)
|
|
77
|
+
|
|
78
|
+
# Set secure file permissions
|
|
79
|
+
os.chmod(STORAGE_FILE, 0o600)
|
|
80
|
+
|
|
81
|
+
# -------------------------------------------------------------------------
|
|
82
|
+
# Command History Methods
|
|
83
|
+
# -------------------------------------------------------------------------
|
|
84
|
+
|
|
85
|
+
def get_command_history(self) -> list[str]:
|
|
86
|
+
"""Get the command history list.
|
|
87
|
+
|
|
88
|
+
Returns:
|
|
89
|
+
List of previously sent messages, most recent last.
|
|
90
|
+
"""
|
|
91
|
+
history = self._data.get("command_history", [])
|
|
92
|
+
if isinstance(history, list):
|
|
93
|
+
return [str(item) for item in cast(list[Any], history)]
|
|
94
|
+
return []
|
|
95
|
+
|
|
96
|
+
def add_command(self, command: str) -> None:
|
|
97
|
+
"""Add a command to history.
|
|
98
|
+
|
|
99
|
+
Maintains a maximum of MAX_COMMAND_HISTORY entries.
|
|
100
|
+
Duplicates are allowed (same message can appear multiple times).
|
|
101
|
+
|
|
102
|
+
Args:
|
|
103
|
+
command: The user message to add to history.
|
|
104
|
+
"""
|
|
105
|
+
command = command.strip()
|
|
106
|
+
if not command:
|
|
107
|
+
return
|
|
108
|
+
|
|
109
|
+
history_data = self._data.get("command_history", [])
|
|
110
|
+
history: list[str]
|
|
111
|
+
if isinstance(history_data, list):
|
|
112
|
+
history = [str(item) for item in cast(list[Any], history_data)]
|
|
113
|
+
else:
|
|
114
|
+
history = []
|
|
115
|
+
history.append(command)
|
|
116
|
+
|
|
117
|
+
# Trim to max size, keeping most recent
|
|
118
|
+
if len(history) > MAX_COMMAND_HISTORY:
|
|
119
|
+
history = history[-MAX_COMMAND_HISTORY:]
|
|
120
|
+
|
|
121
|
+
self._data["command_history"] = history
|
|
122
|
+
self._save()
|
|
123
|
+
|
|
124
|
+
def clear_command_history(self) -> None:
|
|
125
|
+
"""Clear all command history."""
|
|
126
|
+
self._data["command_history"] = []
|
|
127
|
+
self._save()
|
|
128
|
+
|
|
129
|
+
# -------------------------------------------------------------------------
|
|
130
|
+
# Generic Getter/Setter Methods (for future features)
|
|
131
|
+
# -------------------------------------------------------------------------
|
|
132
|
+
|
|
133
|
+
def get(self, section: str, key: str, default: Any = None) -> Any:
|
|
134
|
+
"""Get a value from a storage section.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
section: The storage section (e.g., "preferences", "cache")
|
|
138
|
+
key: The key within the section
|
|
139
|
+
default: Default value if key doesn't exist
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
The stored value or default
|
|
143
|
+
"""
|
|
144
|
+
section_data = self._data.get(section)
|
|
145
|
+
if isinstance(section_data, dict):
|
|
146
|
+
typed_section_data = cast(dict[str, Any], section_data)
|
|
147
|
+
return typed_section_data.get(key, default)
|
|
148
|
+
return default
|
|
149
|
+
|
|
150
|
+
def set(self, section: str, key: str, value: Any) -> None:
|
|
151
|
+
"""Set a value in a storage section.
|
|
152
|
+
|
|
153
|
+
Args:
|
|
154
|
+
section: The storage section (e.g., "preferences", "cache")
|
|
155
|
+
key: The key within the section
|
|
156
|
+
value: The value to store (must be JSON-serializable)
|
|
157
|
+
"""
|
|
158
|
+
if section not in self._data:
|
|
159
|
+
self._data[section] = {}
|
|
160
|
+
|
|
161
|
+
section_data = self._data[section]
|
|
162
|
+
if isinstance(section_data, dict):
|
|
163
|
+
typed_section_data = cast(dict[str, Any], section_data)
|
|
164
|
+
typed_section_data[key] = value
|
|
165
|
+
self._save()
|
|
166
|
+
|
|
167
|
+
def delete(self, section: str, key: str) -> bool:
|
|
168
|
+
"""Delete a key from a storage section.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
section: The storage section
|
|
172
|
+
key: The key to delete
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
True if key was deleted, False if it didn't exist
|
|
176
|
+
"""
|
|
177
|
+
section_data = self._data.get(section)
|
|
178
|
+
if isinstance(section_data, dict) and key in section_data:
|
|
179
|
+
typed_section_data = cast(dict[str, Any], section_data)
|
|
180
|
+
del typed_section_data[key]
|
|
181
|
+
self._save()
|
|
182
|
+
return True
|
|
183
|
+
return False
|
|
184
|
+
|
|
185
|
+
def get_section(self, section: str) -> dict[str, Any]:
|
|
186
|
+
"""Get an entire storage section.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
section: The storage section name
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
The section data as a dict, or empty dict if not found
|
|
193
|
+
"""
|
|
194
|
+
section_data = self._data.get(section)
|
|
195
|
+
if isinstance(section_data, dict):
|
|
196
|
+
typed_section_data = cast(dict[str, Any], section_data)
|
|
197
|
+
return dict(typed_section_data)
|
|
198
|
+
return {}
|
|
199
|
+
|
|
200
|
+
def clear_section(self, section: str) -> None:
|
|
201
|
+
"""Clear all data in a storage section.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
section: The storage section to clear
|
|
205
|
+
"""
|
|
206
|
+
if section in self._data:
|
|
207
|
+
if section == "command_history":
|
|
208
|
+
self._data[section] = []
|
|
209
|
+
else:
|
|
210
|
+
self._data[section] = {}
|
|
211
|
+
self._save()
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
# Singleton instance for convenience
|
|
215
|
+
_storage: LocalStorage | None = None
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def get_storage() -> LocalStorage:
|
|
219
|
+
"""Get the global LocalStorage instance.
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
The singleton LocalStorage instance.
|
|
223
|
+
"""
|
|
224
|
+
global _storage
|
|
225
|
+
if _storage is None:
|
|
226
|
+
_storage = LocalStorage()
|
|
227
|
+
return _storage
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
"""Macro parser for template content.
|
|
2
|
+
|
|
3
|
+
Parses @macro(query) syntax in template content and provides compilation
|
|
4
|
+
to transform macros into valid Jinja2 syntax.
|
|
5
|
+
|
|
6
|
+
Supported macros:
|
|
7
|
+
- @fragment(query) -> {% include 'fragment_name' %}
|
|
8
|
+
- @text(query) -> {{ text('uuid_or_external_id') }}
|
|
9
|
+
- @json(query) -> {{ json('uuid_or_external_id') }}
|
|
10
|
+
- @schema(query) -> {{ schema('name_or_uuid') }}
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import re
|
|
14
|
+
from dataclasses import dataclass
|
|
15
|
+
|
|
16
|
+
# Regex to match @type(query) macros
|
|
17
|
+
# Group 1: macro type (e.g., "fragment", "text", "json", "schema")
|
|
18
|
+
# Group 2: query string (e.g., "header", "article summary")
|
|
19
|
+
MACRO_PATTERN = re.compile(r"@(\w+)\((.*?)\)")
|
|
20
|
+
|
|
21
|
+
# Regex patterns for Jinja2 syntax detection in rendered output
|
|
22
|
+
# These match Jinja2 constructs that survived rendering (e.g., from {% raw %} blocks)
|
|
23
|
+
JINJA_EXPRESSION_PATTERN = re.compile(r"\{\{.*?\}\}", re.DOTALL) # {{ ... }}
|
|
24
|
+
JINJA_STATEMENT_PATTERN = re.compile(r"\{%.*?%\}", re.DOTALL) # {% ... %}
|
|
25
|
+
JINJA_COMMENT_PATTERN = re.compile(r"\{#.*?#\}", re.DOTALL) # {# ... #}
|
|
26
|
+
|
|
27
|
+
# Valid macro types that we support
|
|
28
|
+
VALID_MACRO_TYPES = frozenset({"fragment", "text", "json", "schema"})
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass
|
|
32
|
+
class MacroMatch:
|
|
33
|
+
"""Represents a parsed macro from template content."""
|
|
34
|
+
|
|
35
|
+
macro_type: str
|
|
36
|
+
query: str
|
|
37
|
+
original_text: str
|
|
38
|
+
start: int
|
|
39
|
+
end: int
|
|
40
|
+
|
|
41
|
+
def __repr__(self) -> str:
|
|
42
|
+
return f"MacroMatch({self.macro_type}, {self.query!r})"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def parse_macros(content: str) -> list[MacroMatch]:
|
|
46
|
+
"""Parse all valid macros from template content.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
content: Template content potentially containing macros
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
List of MacroMatch objects for valid macros, in order of appearance
|
|
53
|
+
"""
|
|
54
|
+
matches: list[MacroMatch] = []
|
|
55
|
+
for match in MACRO_PATTERN.finditer(content):
|
|
56
|
+
macro_type = match.group(1)
|
|
57
|
+
query = match.group(2).strip()
|
|
58
|
+
|
|
59
|
+
# Only include valid macro types
|
|
60
|
+
if macro_type in VALID_MACRO_TYPES:
|
|
61
|
+
matches.append(
|
|
62
|
+
MacroMatch(
|
|
63
|
+
macro_type=macro_type,
|
|
64
|
+
query=query,
|
|
65
|
+
original_text=match.group(0),
|
|
66
|
+
start=match.start(),
|
|
67
|
+
end=match.end(),
|
|
68
|
+
)
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
return matches
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def deduplicate_macros(macros: list[MacroMatch]) -> list[MacroMatch]:
|
|
75
|
+
"""Deduplicate macros by (macro_type, query) keeping first occurrence.
|
|
76
|
+
|
|
77
|
+
When the same macro (same type and query) appears multiple times in content,
|
|
78
|
+
this returns only the first occurrence. Useful for resolution flows where
|
|
79
|
+
the user should only need to resolve each unique macro once.
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
macros: List of MacroMatch objects (from parse_macros)
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
List of unique MacroMatch objects, preserving order of first appearance
|
|
86
|
+
"""
|
|
87
|
+
seen: set[tuple[str, str]] = set()
|
|
88
|
+
unique: list[MacroMatch] = []
|
|
89
|
+
for macro in macros:
|
|
90
|
+
key = (macro.macro_type, macro.query)
|
|
91
|
+
if key not in seen:
|
|
92
|
+
seen.add(key)
|
|
93
|
+
unique.append(macro)
|
|
94
|
+
return unique
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def compile_content(content: str, replacements: dict[str, str]) -> str:
|
|
98
|
+
"""Apply macro replacements to template content.
|
|
99
|
+
|
|
100
|
+
Args:
|
|
101
|
+
content: Original template content with macros
|
|
102
|
+
replacements: Mapping of original macro text to replacement Jinja syntax
|
|
103
|
+
|
|
104
|
+
Returns:
|
|
105
|
+
Compiled content with macros replaced
|
|
106
|
+
"""
|
|
107
|
+
result = content
|
|
108
|
+
for original, replacement in replacements.items():
|
|
109
|
+
result = result.replace(original, replacement)
|
|
110
|
+
return result
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def build_jinja_replacement(macro_type: str, resolved_value: str) -> str:
|
|
114
|
+
"""Build the Jinja2 replacement syntax for a resolved macro.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
macro_type: Type of macro (fragment, text, json, schema)
|
|
118
|
+
resolved_value: The resolved value (fragment name, content part UUID/external_id, or schema name/UUID)
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
Valid Jinja2 syntax string
|
|
122
|
+
|
|
123
|
+
Note:
|
|
124
|
+
- fragment uses the template NAME (not UUID) because the renderer resolves by name
|
|
125
|
+
- text/json use UUIDs or external_ids to reference content parts
|
|
126
|
+
- schema uses the schema NAME (or UUID) because the renderer supports both
|
|
127
|
+
"""
|
|
128
|
+
if macro_type == "fragment":
|
|
129
|
+
# Fragment uses name, not UUID
|
|
130
|
+
return f"{{% include '{resolved_value}' %}}"
|
|
131
|
+
elif macro_type == "text":
|
|
132
|
+
return f"{{{{ text('{resolved_value}') }}}}"
|
|
133
|
+
elif macro_type == "json":
|
|
134
|
+
return f"{{{{ json('{resolved_value}') }}}}"
|
|
135
|
+
elif macro_type == "schema":
|
|
136
|
+
return f"{{{{ schema('{resolved_value}') }}}}"
|
|
137
|
+
else:
|
|
138
|
+
raise ValueError(f"Unknown macro type: {macro_type}")
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@dataclass
|
|
142
|
+
class JinjaSyntaxMatch:
|
|
143
|
+
"""Represents a Jinja2 syntax match found in rendered content."""
|
|
144
|
+
|
|
145
|
+
syntax_type: str # "expression", "statement", or "comment"
|
|
146
|
+
content: str # The full matched text including delimiters
|
|
147
|
+
start: int
|
|
148
|
+
end: int
|
|
149
|
+
|
|
150
|
+
def __repr__(self) -> str:
|
|
151
|
+
preview = self.content[:50] + "..." if len(self.content) > 50 else self.content
|
|
152
|
+
return f"JinjaSyntaxMatch({self.syntax_type}, {preview!r})"
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def extract_jinja_syntax(content: str) -> list[JinjaSyntaxMatch]:
|
|
156
|
+
"""Extract all Jinja2 syntax from rendered content.
|
|
157
|
+
|
|
158
|
+
Finds Jinja2 expressions ({{ }}), statements ({% %}), and comments ({# #})
|
|
159
|
+
that remain in the rendered output. These typically come from {% raw %} blocks
|
|
160
|
+
or escaped content that should be preserved for later use.
|
|
161
|
+
|
|
162
|
+
Args:
|
|
163
|
+
content: Rendered template content
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
List of JinjaSyntaxMatch objects in order of appearance, deduplicated
|
|
167
|
+
"""
|
|
168
|
+
matches: list[JinjaSyntaxMatch] = []
|
|
169
|
+
seen_content: set[str] = set()
|
|
170
|
+
|
|
171
|
+
# Find expressions {{ ... }}
|
|
172
|
+
for match in JINJA_EXPRESSION_PATTERN.finditer(content):
|
|
173
|
+
text = match.group(0)
|
|
174
|
+
if text not in seen_content:
|
|
175
|
+
seen_content.add(text)
|
|
176
|
+
matches.append(
|
|
177
|
+
JinjaSyntaxMatch(
|
|
178
|
+
syntax_type="expression",
|
|
179
|
+
content=text,
|
|
180
|
+
start=match.start(),
|
|
181
|
+
end=match.end(),
|
|
182
|
+
)
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
# Find statements {% ... %}
|
|
186
|
+
for match in JINJA_STATEMENT_PATTERN.finditer(content):
|
|
187
|
+
text = match.group(0)
|
|
188
|
+
if text not in seen_content:
|
|
189
|
+
seen_content.add(text)
|
|
190
|
+
matches.append(
|
|
191
|
+
JinjaSyntaxMatch(
|
|
192
|
+
syntax_type="statement",
|
|
193
|
+
content=text,
|
|
194
|
+
start=match.start(),
|
|
195
|
+
end=match.end(),
|
|
196
|
+
)
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
# Find comments {# ... #}
|
|
200
|
+
for match in JINJA_COMMENT_PATTERN.finditer(content):
|
|
201
|
+
text = match.group(0)
|
|
202
|
+
if text not in seen_content:
|
|
203
|
+
seen_content.add(text)
|
|
204
|
+
matches.append(
|
|
205
|
+
JinjaSyntaxMatch(
|
|
206
|
+
syntax_type="comment",
|
|
207
|
+
content=text,
|
|
208
|
+
start=match.start(),
|
|
209
|
+
end=match.end(),
|
|
210
|
+
)
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
# Sort by position in content
|
|
214
|
+
matches.sort(key=lambda m: m.start)
|
|
215
|
+
return matches
|