precis-cli 0.1.3__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.
- app/__init__.py +16 -0
- app/api/__init__.py +32 -0
- app/api/dependencies.py +185 -0
- app/api/main.py +301 -0
- app/api/middleware/__init__.py +34 -0
- app/api/middleware/exception_handler.py +126 -0
- app/api/middleware/request_logging.py +97 -0
- app/api/middleware/token_auth.py +215 -0
- app/api/models/__init__.py +151 -0
- app/api/models/connection_rules.py +98 -0
- app/api/models/files.py +99 -0
- app/api/models/full_validation.py +554 -0
- app/api/models/project.py +117 -0
- app/api/models/projects.py +80 -0
- app/api/models/schema.py +121 -0
- app/api/models/v2_responses.py +46 -0
- app/api/models/validation.py +303 -0
- app/api/models/workspace.py +118 -0
- app/api/routers/__init__.py +56 -0
- app/api/routers/ai/__init__.py +36 -0
- app/api/routers/ai/chat.py +243 -0
- app/api/routers/ai/generate.py +156 -0
- app/api/routers/ai/hardware.py +199 -0
- app/api/routers/ai/jobs.py +544 -0
- app/api/routers/ai/migrate.py +471 -0
- app/api/routers/ai/models.py +352 -0
- app/api/routers/ai/ollama.py +108 -0
- app/api/routers/ai/providers.py +491 -0
- app/api/routers/ai/router.py +44 -0
- app/api/routers/ai/stream.py +281 -0
- app/api/routers/ai/utils.py +182 -0
- app/api/routers/core/__init__.py +34 -0
- app/api/routers/core/connection_rules.py +188 -0
- app/api/routers/core/data_sources.py +374 -0
- app/api/routers/core/regex.py +333 -0
- app/api/routers/core/reporting.py +263 -0
- app/api/routers/files/__init__.py +24 -0
- app/api/routers/files/ops.py +179 -0
- app/api/routers/files/transfer.py +101 -0
- app/api/routers/preview/__init__.py +35 -0
- app/api/routers/preview/content_mode.py +264 -0
- app/api/routers/preview/header_row.py +154 -0
- app/api/routers/preview/models.py +81 -0
- app/api/routers/preview/path_mode.py +440 -0
- app/api/routers/preview/router.py +30 -0
- app/api/routers/project/__init__.py +61 -0
- app/api/routers/project/base.py +111 -0
- app/api/routers/project/constraint.py +345 -0
- app/api/routers/project/full_config.py +411 -0
- app/api/routers/project/full_config_writer.py +327 -0
- app/api/routers/project/helpers.py +167 -0
- app/api/routers/project/inspection_fix.py +330 -0
- app/api/routers/project/manifest.py +605 -0
- app/api/routers/project/models.py +177 -0
- app/api/routers/project/pattern.py +268 -0
- app/api/routers/project/regex.py +358 -0
- app/api/routers/project/scanner.py +157 -0
- app/api/routers/project/schema.py +567 -0
- app/api/routers/project/schema_helpers.py +149 -0
- app/api/routers/project/settings.py +399 -0
- app/api/routers/project/template.py +325 -0
- app/api/routers/project/validation.py +227 -0
- app/api/routers/project/view.py +177 -0
- app/api/routers/project/workspaces.py +167 -0
- app/api/routers/projects/__init__.py +25 -0
- app/api/routers/projects/create.py +121 -0
- app/api/routers/projects/open.py +103 -0
- app/api/routers/projects/scan.py +100 -0
- app/api/routers/validation/__init__.py +39 -0
- app/api/routers/validation/common.py +263 -0
- app/api/routers/validation/content_mode.py +401 -0
- app/api/routers/validation/history.py +235 -0
- app/api/routers/validation/inline_mode.py +196 -0
- app/api/routers/validation/path_mode.py +314 -0
- app/api/routers/validation/router.py +51 -0
- app/api/services/full_validation_response_builder.py +412 -0
- app/api/services/io_error_messages.py +41 -0
- app/api/services/preview_service.py +248 -0
- app/cli/__init__.py +18 -0
- app/cli/__main__.py +30 -0
- app/cli/shared_services/__init__.py +42 -0
- app/cli/shared_services/config_ops.py +581 -0
- app/cli/shared_services/generation_ops.py +160 -0
- app/cli/shared_services/project_ops.py +288 -0
- app/cli/shell/__init__.py +48 -0
- app/cli/shell/commands/__init__.py +63 -0
- app/cli/shell/commands/ai/__init__.py +260 -0
- app/cli/shell/commands/ai/chat.py +331 -0
- app/cli/shell/commands/ai/delete.py +165 -0
- app/cli/shell/commands/ai/diff.py +70 -0
- app/cli/shell/commands/ai/display.py +428 -0
- app/cli/shell/commands/ai/executor.py +225 -0
- app/cli/shell/commands/ai/executor_utils.py +271 -0
- app/cli/shell/commands/ai/generate.py +285 -0
- app/cli/shell/commands/ai/interaction.py +262 -0
- app/cli/shell/commands/ai/migrate.py +281 -0
- app/cli/shell/commands/ai/resolver.py +224 -0
- app/cli/shell/commands/ai/status.py +101 -0
- app/cli/shell/commands/ai/switch.py +158 -0
- app/cli/shell/commands/ai/utils.py +52 -0
- app/cli/shell/commands/base.py +343 -0
- app/cli/shell/commands/config/__init__.py +131 -0
- app/cli/shell/commands/config/base.py +80 -0
- app/cli/shell/commands/config/check.py +246 -0
- app/cli/shell/commands/config/edit.py +148 -0
- app/cli/shell/commands/config/get.py +111 -0
- app/cli/shell/commands/config/init.py +123 -0
- app/cli/shell/commands/config/inspect.py +164 -0
- app/cli/shell/commands/config/list.py +104 -0
- app/cli/shell/commands/config/set.py +119 -0
- app/cli/shell/commands/config/show.py +150 -0
- app/cli/shell/commands/exit.py +67 -0
- app/cli/shell/commands/help.py +104 -0
- app/cli/shell/commands/infer_schema.py +156 -0
- app/cli/shell/commands/open.py +201 -0
- app/cli/shell/commands/project.py +215 -0
- app/cli/shell/commands/provider.py +619 -0
- app/cli/shell/commands/system.py +170 -0
- app/cli/shell/commands/validate.py +455 -0
- app/cli/shell/completer.py +231 -0
- app/cli/shell/config_storage.py +191 -0
- app/cli/shell/exceptions.py +90 -0
- app/cli/shell/formatter.py +371 -0
- app/cli/shell/interactive_menu.py +348 -0
- app/cli/shell/main.py +286 -0
- app/cli/shell/parser.py +266 -0
- app/cli/start.py +180 -0
- app/cli_main.py +52 -0
- app/mcp_server.py +311 -0
- app/shared/core/__init__.py +18 -0
- app/shared/core/app_version.py +59 -0
- app/shared/core/config/__init__.py +119 -0
- app/shared/core/config/server.py +193 -0
- app/shared/core/data_source/__init__.py +106 -0
- app/shared/core/data_source/loader.py +348 -0
- app/shared/core/data_source/loaders/__init__.py +213 -0
- app/shared/core/data_source/loaders/base.py +215 -0
- app/shared/core/data_source/loaders/converter.py +337 -0
- app/shared/core/data_source/loaders/csv_loader.py +252 -0
- app/shared/core/data_source/loaders/excel_loader.py +666 -0
- app/shared/core/data_source/loaders/extractor.py +268 -0
- app/shared/core/data_source/loaders/json_loader.py +354 -0
- app/shared/core/data_source/loaders/registry.py +88 -0
- app/shared/core/data_source/loaders/sql_loader.py +313 -0
- app/shared/core/data_source/loaders/strategies/__init__.py +100 -0
- app/shared/core/data_source/loaders/strategies/array_parser.py +277 -0
- app/shared/core/data_source/loaders/strategies/lines_parser.py +355 -0
- app/shared/core/data_source/loaders/strategies/object_parser.py +278 -0
- app/shared/core/data_source/schema_info.py +50 -0
- app/shared/core/data_source/specs/__init__.py +128 -0
- app/shared/core/data_source/specs/base.py +258 -0
- app/shared/core/data_source/specs/csv_source.py +137 -0
- app/shared/core/data_source/specs/excel_source.py +141 -0
- app/shared/core/data_source/specs/file_base.py +252 -0
- app/shared/core/data_source/specs/json_source.py +226 -0
- app/shared/core/data_source/specs/sql_source.py +161 -0
- app/shared/core/encoding.py +36 -0
- app/shared/core/io/__init__.py +24 -0
- app/shared/core/io/yaml.py +379 -0
- app/shared/core/manifest_schema/__init__.py +52 -0
- app/shared/core/manifest_schema/types.py +99 -0
- app/shared/core/manifest_schema/version.py +175 -0
- app/shared/core/patterns/__init__.py +24 -0
- app/shared/core/patterns/loader.py +134 -0
- app/shared/core/patterns/writer.py +244 -0
- app/shared/core/project/__init__.py +47 -0
- app/shared/core/project/constraint/builders/__init__.py +50 -0
- app/shared/core/project/constraint/builders/base.py +100 -0
- app/shared/core/project/constraint/builders/composite.py +77 -0
- app/shared/core/project/constraint/builders/conditional.py +67 -0
- app/shared/core/project/constraint/builders/foreign_key.py +53 -0
- app/shared/core/project/constraint/builders/registry.py +64 -0
- app/shared/core/project/constraint/builders/scripted.py +51 -0
- app/shared/core/project/constraint/builders/single_column.py +86 -0
- app/shared/core/project/constraint/builders/unique.py +53 -0
- app/shared/core/project/constraint/factory.py +170 -0
- app/shared/core/project/constraint/reader.py +214 -0
- app/shared/core/project/constraint/registry.py +233 -0
- app/shared/core/project/constraint/types/__init__.py +63 -0
- app/shared/core/project/constraint/types/constraint_file.py +261 -0
- app/shared/core/project/constraint/types/refs.py +460 -0
- app/shared/core/project/constraint/types.py +28 -0
- app/shared/core/project/constraint/writer.py +181 -0
- app/shared/core/project/loader/__init__.py +56 -0
- app/shared/core/project/loader/loader.py +30 -0
- app/shared/core/project/loader/loader_parts/config_inspector.py +137 -0
- app/shared/core/project/loader/loader_parts/embedded_constraints.py +224 -0
- app/shared/core/project/loader/loader_parts/file_loaders.py +58 -0
- app/shared/core/project/loader/loader_parts/inspection_ids.py +85 -0
- app/shared/core/project/loader/loader_parts/inspector_helpers.py +312 -0
- app/shared/core/project/loader/loader_parts/inspector_id_checks.py +274 -0
- app/shared/core/project/loader/loader_parts/inspector_reference_checks.py +690 -0
- app/shared/core/project/loader/loader_parts/inspector_uniqueness_checks.py +286 -0
- app/shared/core/project/loader/loader_parts/loading_error_messages.py +298 -0
- app/shared/core/project/loader/loader_parts/main.py +432 -0
- app/shared/core/project/loader/loader_parts/path_validation.py +117 -0
- app/shared/core/project/loader/loader_parts/runtime.py +125 -0
- app/shared/core/project/loader/types.py +246 -0
- app/shared/core/project/manifest/coverage.py +391 -0
- app/shared/core/project/manifest/reader.py +260 -0
- app/shared/core/project/manifest/types.py +91 -0
- app/shared/core/project/manifest/types_parts/__init__.py +60 -0
- app/shared/core/project/manifest/types_parts/constants.py +40 -0
- app/shared/core/project/manifest/types_parts/data_source.py +68 -0
- app/shared/core/project/manifest/types_parts/info.py +59 -0
- app/shared/core/project/manifest/types_parts/manifest.py +294 -0
- app/shared/core/project/manifest/types_parts/refs.py +153 -0
- app/shared/core/project/manifest/types_parts/settings.py +92 -0
- app/shared/core/project/manifest/types_parts/settings_file_processing.py +64 -0
- app/shared/core/project/manifest/types_parts/settings_script_security.py +78 -0
- app/shared/core/project/manifest/types_parts/settings_validation.py +83 -0
- app/shared/core/project/manifest/types_parts/template.py +66 -0
- app/shared/core/project/manifest/writer.py +262 -0
- app/shared/core/project/manual_data/__init__.py +27 -0
- app/shared/core/project/manual_data/types.py +75 -0
- app/shared/core/project/regex/reader.py +197 -0
- app/shared/core/project/regex/types.py +405 -0
- app/shared/core/project/regex/writer.py +123 -0
- app/shared/core/project/schema/reader.py +170 -0
- app/shared/core/project/schema/types.py +47 -0
- app/shared/core/project/schema/types_parts/__init__.py +36 -0
- app/shared/core/project/schema/types_parts/column.py +174 -0
- app/shared/core/project/schema/types_parts/column_utils.py +72 -0
- app/shared/core/project/schema/types_parts/constraint.py +165 -0
- app/shared/core/project/schema/types_parts/schema_id.py +66 -0
- app/shared/core/project/schema/types_parts/source.py +255 -0
- app/shared/core/project/schema/types_parts/source_options.py +347 -0
- app/shared/core/project/schema/types_parts/table.py +230 -0
- app/shared/core/project/schema/writer.py +139 -0
- app/shared/core/project/schema_ref_check.py +95 -0
- app/shared/core/project/template/__init__.py +27 -0
- app/shared/core/project/template/expander.py +263 -0
- app/shared/core/project/template/reader.py +120 -0
- app/shared/core/project/template/types.py +114 -0
- app/shared/core/project/transform/reader.py +76 -0
- app/shared/core/project/transform/types.py +116 -0
- app/shared/core/project/transform/writer.py +84 -0
- app/shared/core/pydantic_messages.py +59 -0
- app/shared/core/reporter/__init__.py +46 -0
- app/shared/core/reporter/reporter.py +220 -0
- app/shared/core/reporter/reporters/__init__.py +65 -0
- app/shared/core/reporter/reporters/base.py +188 -0
- app/shared/core/reporter/reporters/dingtalk_app_reporter.py +274 -0
- app/shared/core/reporter/reporters/email_reporter.py +271 -0
- app/shared/core/reporter/reporters/feishu_app_reporter.py +467 -0
- app/shared/core/reporter/reporters/local_file_reporter.py +208 -0
- app/shared/core/reporter/reporters/wecom_app_reporter.py +268 -0
- app/shared/core/utils/__init__.py +18 -0
- app/shared/core/utils/path_utils.py +60 -0
- app/shared/core/utils/regex_extract.py +113 -0
- app/shared/domain/__init__.py +114 -0
- app/shared/domain/constraints/__init__.py +76 -0
- app/shared/domain/constraints/allowed_values.py +211 -0
- app/shared/domain/constraints/base.py +141 -0
- app/shared/domain/constraints/charset.py +337 -0
- app/shared/domain/constraints/composite.py +174 -0
- app/shared/domain/constraints/condition_registry.py +130 -0
- app/shared/domain/constraints/conditional.py +629 -0
- app/shared/domain/constraints/date_logic.py +731 -0
- app/shared/domain/constraints/foreign_key.py +261 -0
- app/shared/domain/constraints/key_normalization.py +56 -0
- app/shared/domain/constraints/not_null.py +185 -0
- app/shared/domain/constraints/range.py +360 -0
- app/shared/domain/constraints/regex.py +218 -0
- app/shared/domain/constraints/scripted.py +276 -0
- app/shared/domain/constraints/unique.py +233 -0
- app/shared/domain/data_engine.py +384 -0
- app/shared/domain/data_types.py +113 -0
- app/shared/domain/data_types_parts/__init__.py +67 -0
- app/shared/domain/data_types_parts/base.py +204 -0
- app/shared/domain/data_types_parts/composite.py +250 -0
- app/shared/domain/data_types_parts/expression.py +202 -0
- app/shared/domain/data_types_parts/extracted.py +106 -0
- app/shared/domain/data_types_parts/json_types.py +234 -0
- app/shared/domain/data_types_parts/scalars.py +719 -0
- app/shared/domain/data_types_parts/sequence.py +129 -0
- app/shared/domain/dataset_schema.py +48 -0
- app/shared/domain/eval_sandbox.py +63 -0
- app/shared/domain/expression_system.py +366 -0
- app/shared/domain/regex_flags.py +53 -0
- app/shared/domain/schema/builder.py +223 -0
- app/shared/domain/schema/models.py +339 -0
- app/shared/domain/transforms/__init__.py +28 -0
- app/shared/domain/transforms/aggregate.py +141 -0
- app/shared/domain/transforms/base.py +171 -0
- app/shared/domain/transforms/cast_type.py +120 -0
- app/shared/domain/transforms/concat.py +103 -0
- app/shared/domain/transforms/conditional_assign.py +114 -0
- app/shared/domain/transforms/date_format.py +81 -0
- app/shared/domain/transforms/digits.py +77 -0
- app/shared/domain/transforms/drop_duplicates.py +94 -0
- app/shared/domain/transforms/fill_na.py +94 -0
- app/shared/domain/transforms/filter_rows.py +97 -0
- app/shared/domain/transforms/lookup.py +78 -0
- app/shared/domain/transforms/lower_case.py +70 -0
- app/shared/domain/transforms/map_value.py +90 -0
- app/shared/domain/transforms/math_expr.py +112 -0
- app/shared/domain/transforms/modulo.py +82 -0
- app/shared/domain/transforms/regex_extract.py +103 -0
- app/shared/domain/transforms/registry.py +95 -0
- app/shared/domain/transforms/replace.py +88 -0
- app/shared/domain/transforms/sort_rows.py +94 -0
- app/shared/domain/transforms/string_split.py +84 -0
- app/shared/domain/transforms/strip.py +73 -0
- app/shared/domain/transforms/substring.py +92 -0
- app/shared/domain/transforms/upper_case.py +70 -0
- app/shared/domain/transforms/weighted_sum.py +104 -0
- app/shared/domain/validation_constraints.py +69 -0
- app/shared/services/__init__.py +62 -0
- app/shared/services/ai/__init__.py +48 -0
- app/shared/services/ai/agent/__init__.py +40 -0
- app/shared/services/ai/agent/chat_tools/__init__.py +47 -0
- app/shared/services/ai/agent/chat_tools/apply_actions.py +592 -0
- app/shared/services/ai/agent/chat_tools/ask_user.py +237 -0
- app/shared/services/ai/agent/chat_tools/read_canvas.py +140 -0
- app/shared/services/ai/agent/chat_tools/read_project.py +160 -0
- app/shared/services/ai/agent/chat_tools/read_table.py +301 -0
- app/shared/services/ai/agent/chat_tools/schemas.py +105 -0
- app/shared/services/ai/agent/chat_tools/validate_table.py +131 -0
- app/shared/services/ai/agent/executor.py +554 -0
- app/shared/services/ai/agent/memory.py +256 -0
- app/shared/services/ai/agent/planner.py +282 -0
- app/shared/services/ai/agent/tool_registry.py +296 -0
- app/shared/services/ai/agent/tools/__init__.py +32 -0
- app/shared/services/ai/agent/tools/config_generate.py +133 -0
- app/shared/services/ai/agent/tools/config_refine.py +109 -0
- app/shared/services/ai/agent/tools/config_validate.py +403 -0
- app/shared/services/ai/agent/tools/merge_results.py +136 -0
- app/shared/services/ai/agent/tools/plan_chunks.py +79 -0
- app/shared/services/ai/agent/tools/script_parse.py +359 -0
- app/shared/services/ai/agent/types.py +138 -0
- app/shared/services/ai/chat_agent_runner.py +596 -0
- app/shared/services/ai/chat_orchestrator.py +725 -0
- app/shared/services/ai/failure_messages.py +65 -0
- app/shared/services/ai/job_storage.py +274 -0
- app/shared/services/ai/migrate_service.py +550 -0
- app/shared/services/ai/streaming/__init__.py +65 -0
- app/shared/services/ai/streaming/event_journal.py +197 -0
- app/shared/services/ai/streaming/orchestrator.py +262 -0
- app/shared/services/ai/streaming/pending_interaction_store.py +227 -0
- app/shared/services/ai/streaming/sse_response.py +148 -0
- app/shared/services/ai/streaming/types.py +73 -0
- app/shared/services/ai/types.py +213 -0
- app/shared/services/ai/utils.py +322 -0
- app/shared/services/diff/config_diff.py +320 -0
- app/shared/services/hardware.py +237 -0
- app/shared/services/llm/__init__.py +62 -0
- app/shared/services/llm/actions/__init__.py +42 -0
- app/shared/services/llm/actions/_canvas_validator.py +147 -0
- app/shared/services/llm/actions/_constraint_validator.py +401 -0
- app/shared/services/llm/actions/_regex_validator.py +85 -0
- app/shared/services/llm/actions/_schema_validator.py +82 -0
- app/shared/services/llm/actions/_settings_validator.py +76 -0
- app/shared/services/llm/actions/_transform_validator.py +78 -0
- app/shared/services/llm/actions/action_handlers.py +400 -0
- app/shared/services/llm/actions/action_parser.py +63 -0
- app/shared/services/llm/actions/action_processor.py +468 -0
- app/shared/services/llm/actions/action_validator.py +311 -0
- app/shared/services/llm/actions/diff_compute.py +195 -0
- app/shared/services/llm/actions/regex_handlers.py +284 -0
- app/shared/services/llm/actions/registry.py +336 -0
- app/shared/services/llm/actions/schema_handlers.py +335 -0
- app/shared/services/llm/actions/settings_handlers.py +180 -0
- app/shared/services/llm/actions/specs.py +259 -0
- app/shared/services/llm/actions/transform_handlers.py +279 -0
- app/shared/services/llm/actions/validation_types.py +147 -0
- app/shared/services/llm/cache/__init__.py +18 -0
- app/shared/services/llm/cache/response_cache.py +80 -0
- app/shared/services/llm/chat/__init__.py +35 -0
- app/shared/services/llm/chat/chat_system_prompt.py +561 -0
- app/shared/services/llm/chat/response_parser.py +296 -0
- app/shared/services/llm/config/__init__.py +45 -0
- app/shared/services/llm/config/crypto.py +135 -0
- app/shared/services/llm/config/loader.py +258 -0
- app/shared/services/llm/config/models.py +202 -0
- app/shared/services/llm/config/presets.py +128 -0
- app/shared/services/llm/config_generator.py +163 -0
- app/shared/services/llm/constraints/__init__.py +41 -0
- app/shared/services/llm/constraints/constraint_builder.py +177 -0
- app/shared/services/llm/constraints/constraint_deletion.py +84 -0
- app/shared/services/llm/constraints/constraint_id.py +174 -0
- app/shared/services/llm/constraints/frontend_instructions.py +333 -0
- app/shared/services/llm/constraints/inline_batch.py +279 -0
- app/shared/services/llm/discovery/__init__.py +35 -0
- app/shared/services/llm/discovery/scanner.py +181 -0
- app/shared/services/llm/generation/__init__.py +42 -0
- app/shared/services/llm/generation/agent_wiring.py +156 -0
- app/shared/services/llm/generation/config_builder.py +386 -0
- app/shared/services/llm/generation/errors.py +36 -0
- app/shared/services/llm/generation/existing_config.py +99 -0
- app/shared/services/llm/generation/profiler.py +279 -0
- app/shared/services/llm/generation/prompt_builder.py +199 -0
- app/shared/services/llm/generation/response_parser.py +144 -0
- app/shared/services/llm/generation/service.py +622 -0
- app/shared/services/llm/models.py +104 -0
- app/shared/services/llm/providers/__init__.py +48 -0
- app/shared/services/llm/providers/base.py +310 -0
- app/shared/services/llm/providers/cached_provider.py +90 -0
- app/shared/services/llm/providers/ollama.py +498 -0
- app/shared/services/llm/providers/openai.py +334 -0
- app/shared/services/llm/providers/registry.py +108 -0
- app/shared/services/llm/schema_resolver.py +141 -0
- app/shared/services/llm/suggestion_utils.py +219 -0
- app/shared/services/llm/validate_executor.py +163 -0
- app/shared/services/llm/yaml_io.py +406 -0
- app/shared/services/preview/__init__.py +18 -0
- app/shared/services/preview/loader.py +145 -0
- app/shared/services/preview/path_validation.py +138 -0
- app/shared/services/project_loader.py +57 -0
- app/shared/services/schema_inference.py +260 -0
- app/shared/services/schema_runtime_builder.py +120 -0
- app/shared/services/validation/__init__.py +52 -0
- app/shared/services/validation/chunked_loader.py +509 -0
- app/shared/services/validation/dag/__init__.py +35 -0
- app/shared/services/validation/dag/builder.py +141 -0
- app/shared/services/validation/dag/executor.py +225 -0
- app/shared/services/validation/dag/sorter.py +77 -0
- app/shared/services/validation/data_loader.py +231 -0
- app/shared/services/validation/engine.py +446 -0
- app/shared/services/validation/executor.py +1030 -0
- app/shared/services/validation/extractors.py +266 -0
- app/shared/services/validation/history.py +209 -0
- app/shared/services/validation/json_payload.py +136 -0
- app/shared/services/validation/loader.py +152 -0
- app/shared/services/validation/memory_monitor.py +179 -0
- app/shared/services/validation/postprocess.py +208 -0
- app/shared/services/validation/progress.py +64 -0
- app/shared/services/validation/report_export.py +222 -0
- app/shared/services/validation/resolver.py +180 -0
- app/shared/services/validation/service.py +418 -0
- app/shared/services/validation/types.py +238 -0
- app/shared/services/validation/validators/__init__.py +36 -0
- app/shared/services/validation/validators/adapter.py +182 -0
- app/shared/services/validation/validators/base.py +332 -0
- app/shared/services/validation/validators/composite.py +233 -0
- app/shared/services/validation/validators/date_logic.py +276 -0
- app/start_server.py +133 -0
- precis_cli-0.1.3.dist-info/METADATA +180 -0
- precis_cli-0.1.3.dist-info/RECORD +442 -0
- precis_cli-0.1.3.dist-info/WHEEL +5 -0
- precis_cli-0.1.3.dist-info/entry_points.txt +4 -0
- precis_cli-0.1.3.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2026 Precis Team
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
# backend/app/cli/shared_services/generation_ops.py
|
|
17
|
+
"""
|
|
18
|
+
@fileoverview 生成/迁移配置落盘共享逻辑(CLI/TUI 同源)
|
|
19
|
+
|
|
20
|
+
功能概述:
|
|
21
|
+
- 收敛 AI 配置生成与迁移的纯业务逻辑:生成结果落盘、数据文件扫描与展开
|
|
22
|
+
- 供 CLI 的 ai generate / ai migrate 命令与未来 TUI 的生成/迁移屏共同调用
|
|
23
|
+
|
|
24
|
+
架构设计:
|
|
25
|
+
- 本模块只含纯逻辑与文件 IO,不含任何 UI/交互
|
|
26
|
+
- apply_generated_config 复用 V2 配置写入约定(project.precis.yaml + schemas/ + constraints/ + regex/)
|
|
27
|
+
- scan_data_files 统一 patterns 展开与 data/ 目录扫描两种入口
|
|
28
|
+
|
|
29
|
+
接口契约(P0b 冻结):
|
|
30
|
+
def apply_generated_config(result: dict, project_path: str) -> list[str]
|
|
31
|
+
def scan_data_files(patterns: list[str], project_path: str) -> list[str]
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
from __future__ import annotations
|
|
35
|
+
|
|
36
|
+
import glob
|
|
37
|
+
import logging
|
|
38
|
+
import os
|
|
39
|
+
from pathlib import Path
|
|
40
|
+
from typing import Any
|
|
41
|
+
|
|
42
|
+
from app.shared.core.io.yaml import read_yaml, write_yaml_atomic
|
|
43
|
+
|
|
44
|
+
logger = logging.getLogger(__name__)
|
|
45
|
+
|
|
46
|
+
# 支持的文件扩展名(generate/migrate 共用)
|
|
47
|
+
SUPPORTED_EXTENSIONS = (".xlsx", ".xls", ".csv", ".json", ".jsonl")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def scan_data_files(patterns: list[str], project_path: str) -> list[str]:
|
|
51
|
+
"""展开数据文件路径(支持通配符、相对路径、data/ 目录扫描)。
|
|
52
|
+
|
|
53
|
+
逻辑:
|
|
54
|
+
1. 若 patterns 非空:逐个展开通配符 / 直连存在的路径,合并去重
|
|
55
|
+
2. 若 patterns 为空(或展开后为空):扫描项目 data/ 目录下的支持文件
|
|
56
|
+
3. 最终按 SUPPORTED_EXTENSIONS 过滤
|
|
57
|
+
|
|
58
|
+
Args:
|
|
59
|
+
patterns: 用户输入的文件模式列表(可为相对项目根的路径或通配符)
|
|
60
|
+
project_path: 项目根目录绝对路径
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
绝对路径列表(已去重、已排序的 data/ 扫描部分 + 保持展开顺序的 patterns 部分)
|
|
64
|
+
"""
|
|
65
|
+
file_paths: list[str] = []
|
|
66
|
+
for pattern in patterns:
|
|
67
|
+
if not os.path.isabs(pattern):
|
|
68
|
+
pattern = os.path.join(project_path, pattern)
|
|
69
|
+
matched = glob.glob(pattern)
|
|
70
|
+
if matched:
|
|
71
|
+
file_paths.extend(matched)
|
|
72
|
+
elif os.path.exists(pattern):
|
|
73
|
+
file_paths.append(pattern)
|
|
74
|
+
|
|
75
|
+
# 合并去重(保序)
|
|
76
|
+
file_paths = list(dict.fromkeys(file_paths))
|
|
77
|
+
|
|
78
|
+
# 无文件参数时扫描 data/ 目录(保持原 _scan_data_files 行为)
|
|
79
|
+
if not file_paths:
|
|
80
|
+
data_dir = Path(project_path) / "data"
|
|
81
|
+
if data_dir.exists():
|
|
82
|
+
for ext in SUPPORTED_EXTENSIONS:
|
|
83
|
+
file_paths.extend(str(p) for p in data_dir.glob(f"*{ext}"))
|
|
84
|
+
# 扫描分支输出排序,保证结果稳定
|
|
85
|
+
file_paths.sort()
|
|
86
|
+
|
|
87
|
+
# 过滤支持的文件类型
|
|
88
|
+
return [p for p in file_paths if p.lower().endswith(SUPPORTED_EXTENSIONS)]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def apply_generated_config(result: dict[str, Any], project_path: str) -> list[str]:
|
|
92
|
+
"""将生成的配置写入项目目录。
|
|
93
|
+
|
|
94
|
+
会保留现有 project.precis.yaml 中的 transforms/manual_data 等引用,
|
|
95
|
+
覆盖写入 schemas、constraints、regex_nodes。
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
result: ConfigGenerationService / ConfigMigrationService 返回的配置字典
|
|
99
|
+
project_path: 项目根目录
|
|
100
|
+
|
|
101
|
+
Returns:
|
|
102
|
+
已写入的文件相对路径列表(manifest + 各 schema/constraint/regex 文件)
|
|
103
|
+
"""
|
|
104
|
+
written: list[str] = []
|
|
105
|
+
|
|
106
|
+
manifest_path = Path(project_path) / "project.precis.yaml"
|
|
107
|
+
existing_manifest: dict[str, Any] = {}
|
|
108
|
+
if manifest_path.exists():
|
|
109
|
+
try:
|
|
110
|
+
existing_manifest = read_yaml(manifest_path) or {}
|
|
111
|
+
except Exception:
|
|
112
|
+
logger.warning("读取现有 manifest 失败,将覆盖写入", exc_info=True)
|
|
113
|
+
|
|
114
|
+
manifest = result.get("manifest") or {"version": 2, "project": {"id": "", "name": ""}}
|
|
115
|
+
|
|
116
|
+
# 保留现有 manifest 中生成未覆盖的引用
|
|
117
|
+
for key in ("transforms", "manual_data"):
|
|
118
|
+
if key in existing_manifest and key not in manifest:
|
|
119
|
+
manifest[key] = existing_manifest[key]
|
|
120
|
+
|
|
121
|
+
schemas = result.get("schemas", {})
|
|
122
|
+
constraints = result.get("constraints", {})
|
|
123
|
+
regex_nodes = result.get("regex_nodes", {})
|
|
124
|
+
|
|
125
|
+
# 确保目录存在
|
|
126
|
+
(Path(project_path) / "schemas").mkdir(exist_ok=True)
|
|
127
|
+
(Path(project_path) / "constraints").mkdir(exist_ok=True)
|
|
128
|
+
(Path(project_path) / "regex").mkdir(exist_ok=True)
|
|
129
|
+
|
|
130
|
+
# 更新 manifest 引用
|
|
131
|
+
manifest["schemas"] = [{"id": sid, "path": f"schemas/{sid}.schema.yaml"} for sid in schemas]
|
|
132
|
+
manifest["constraints"] = [{"id": cid, "path": f"constraints/{cid}.constraint.yaml"} for cid in constraints]
|
|
133
|
+
manifest["regex_nodes"] = [{"id": rid, "path": f"regex/{rid}.regex.yaml"} for rid in regex_nodes]
|
|
134
|
+
|
|
135
|
+
# 写入 manifest
|
|
136
|
+
write_yaml_atomic(manifest_path, manifest)
|
|
137
|
+
written.append("project.precis.yaml")
|
|
138
|
+
|
|
139
|
+
# 写入资源文件
|
|
140
|
+
for sid, schema in schemas.items():
|
|
141
|
+
rel = f"schemas/{sid}.schema.yaml"
|
|
142
|
+
write_yaml_atomic(Path(project_path) / rel, schema)
|
|
143
|
+
written.append(rel)
|
|
144
|
+
for cid, constraint in constraints.items():
|
|
145
|
+
rel = f"constraints/{cid}.constraint.yaml"
|
|
146
|
+
write_yaml_atomic(Path(project_path) / rel, constraint)
|
|
147
|
+
written.append(rel)
|
|
148
|
+
for rid, regex_node in regex_nodes.items():
|
|
149
|
+
rel = f"regex/{rid}.regex.yaml"
|
|
150
|
+
write_yaml_atomic(Path(project_path) / rel, regex_node)
|
|
151
|
+
written.append(rel)
|
|
152
|
+
|
|
153
|
+
return written
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
__all__ = [
|
|
157
|
+
"SUPPORTED_EXTENSIONS",
|
|
158
|
+
"apply_generated_config",
|
|
159
|
+
"scan_data_files",
|
|
160
|
+
]
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2026 Precis Team
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
# backend/app/cli/shared_services/project_ops.py
|
|
17
|
+
"""
|
|
18
|
+
@fileoverview 项目操作共享逻辑(CLI/TUI 同源)
|
|
19
|
+
|
|
20
|
+
功能概述:
|
|
21
|
+
- 收敛「打开项目」相关的纯业务逻辑:历史记录读写、清单定位与加载、项目显示名解析
|
|
22
|
+
- 供 CLI 的 open 命令与未来 TUI 的项目管理屏共同调用,确保「改一处即可」
|
|
23
|
+
|
|
24
|
+
架构设计:
|
|
25
|
+
- 本模块只含纯逻辑与文件 IO,不含任何 UI/交互(InteractiveMenu 等留在各 UI 层)
|
|
26
|
+
- 历史记录持久化到 ~/.precis_project_history,去重 + MAX_HISTORY 容量上限
|
|
27
|
+
- 清单加载复用 shared/core 的 load_manifest,保证解析与系统其余部分一致
|
|
28
|
+
|
|
29
|
+
接口契约(P0b 冻结):
|
|
30
|
+
def load_history() -> list[dict]
|
|
31
|
+
def add_to_history(project_path: str) -> None
|
|
32
|
+
def find_manifest(project_path: str) -> str | None
|
|
33
|
+
def load_manifest_config(manifest_path: str) -> dict | None
|
|
34
|
+
def resolve_project_label(project_path: str) -> str
|
|
35
|
+
def open_project(project_path: str) -> OpenResult
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
from __future__ import annotations
|
|
39
|
+
|
|
40
|
+
import json
|
|
41
|
+
import logging
|
|
42
|
+
import os
|
|
43
|
+
from dataclasses import dataclass
|
|
44
|
+
from datetime import UTC, datetime
|
|
45
|
+
|
|
46
|
+
logger = logging.getLogger(__name__)
|
|
47
|
+
|
|
48
|
+
# 历史记录文件路径:存储在用户主目录下
|
|
49
|
+
HISTORY_FILE = os.path.expanduser("~/.precis_project_history")
|
|
50
|
+
# 最大历史记录数量,超过时会自动丢弃最旧的项目
|
|
51
|
+
MAX_HISTORY = 10
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def load_history() -> list[dict]:
|
|
55
|
+
"""加载项目打开历史。
|
|
56
|
+
|
|
57
|
+
从 HISTORY_FILE 读取历史记录。文件不存在、格式损坏或权限不足时返回空列表。
|
|
58
|
+
|
|
59
|
+
Returns:
|
|
60
|
+
历史记录列表,每个元素为包含 path 和 last_opened 的字典
|
|
61
|
+
"""
|
|
62
|
+
if not os.path.exists(HISTORY_FILE):
|
|
63
|
+
return []
|
|
64
|
+
try:
|
|
65
|
+
with open(HISTORY_FILE, encoding="utf-8") as f:
|
|
66
|
+
data = json.load(f)
|
|
67
|
+
if isinstance(data, list):
|
|
68
|
+
# 4.8: 条目级健壮性——磁盘问题/手改造成的非 dict 条目(如纯字符串)
|
|
69
|
+
# 会让 open 命令在 item.get 处 AttributeError 崩溃(一个项目都打不开),
|
|
70
|
+
# 跳过并累计警告
|
|
71
|
+
valid = [h for h in data if isinstance(h, dict) and h.get("path")]
|
|
72
|
+
if len(valid) != len(data):
|
|
73
|
+
logger.warning(
|
|
74
|
+
"忽略 %d 条损坏的历史记录(非对象或缺 path 字段): %s",
|
|
75
|
+
len(data) - len(valid),
|
|
76
|
+
HISTORY_FILE,
|
|
77
|
+
)
|
|
78
|
+
return valid
|
|
79
|
+
return []
|
|
80
|
+
except json.JSONDecodeError:
|
|
81
|
+
# JSON 格式损坏,返回空列表
|
|
82
|
+
return []
|
|
83
|
+
except PermissionError:
|
|
84
|
+
# 无权限读取,返回空列表
|
|
85
|
+
return []
|
|
86
|
+
except OSError:
|
|
87
|
+
# 其他操作系统错误(如文件被锁定),返回空列表
|
|
88
|
+
return []
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _save_history(history: list[dict]) -> None:
|
|
92
|
+
"""保存项目打开历史。
|
|
93
|
+
|
|
94
|
+
将历史记录写入 HISTORY_FILE。保存失败仅记录日志,不阻断主流程。
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
history: 历史记录列表
|
|
98
|
+
"""
|
|
99
|
+
try:
|
|
100
|
+
# 确保目录存在(用户主目录)
|
|
101
|
+
os.makedirs(os.path.dirname(HISTORY_FILE), exist_ok=True)
|
|
102
|
+
with open(HISTORY_FILE, "w", encoding="utf-8") as f:
|
|
103
|
+
json.dump(history, f, ensure_ascii=False, indent=2)
|
|
104
|
+
except (PermissionError, OSError):
|
|
105
|
+
# 保存失败时记录错误日志,但不阻断主流程
|
|
106
|
+
logger.error("保存项目历史记录失败", exc_info=True)
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def add_to_history(project_path: str) -> None:
|
|
110
|
+
"""将项目路径添加到历史记录顶部。
|
|
111
|
+
|
|
112
|
+
如果路径已存在,先移除旧记录,再插入到顶部。超过 MAX_HISTORY 时自动截断。
|
|
113
|
+
|
|
114
|
+
Args:
|
|
115
|
+
project_path: 项目的绝对路径
|
|
116
|
+
"""
|
|
117
|
+
history = load_history()
|
|
118
|
+
# 去重:移除已存在的相同路径
|
|
119
|
+
history = [h for h in history if h.get("path") != project_path]
|
|
120
|
+
# 插入到列表头部(最新的项目),并记录真实打开时间(UTC ISO 格式)。
|
|
121
|
+
# 历史展示顺序即插入序(最新在前),last_opened 仅供人读/审计,不参与排序。
|
|
122
|
+
history.insert(0, {"path": project_path, "last_opened": datetime.now(UTC).isoformat()})
|
|
123
|
+
# 容量控制:只保留最近的 MAX_HISTORY 个项目
|
|
124
|
+
if len(history) > MAX_HISTORY:
|
|
125
|
+
history = history[:MAX_HISTORY]
|
|
126
|
+
_save_history(history)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def find_manifest(project_path: str) -> str | None:
|
|
130
|
+
"""定位项目清单文件,兼容 .yaml / .yml 后缀。
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
project_path: 项目根目录绝对路径
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
清单文件绝对路径;不存在则返回 None
|
|
137
|
+
"""
|
|
138
|
+
# 优先 .yaml 后缀(项目规范)
|
|
139
|
+
primary = os.path.join(project_path, "project.precis.yaml")
|
|
140
|
+
if os.path.isfile(primary):
|
|
141
|
+
return primary
|
|
142
|
+
# 兼容 .yml 后缀
|
|
143
|
+
alt = os.path.join(project_path, "project.precis.yml")
|
|
144
|
+
if os.path.isfile(alt):
|
|
145
|
+
return alt
|
|
146
|
+
return None
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def load_manifest_config(manifest_path: str) -> dict | None:
|
|
150
|
+
"""加载并序列化清单为字典。
|
|
151
|
+
|
|
152
|
+
复用 shared/core 中的 load_manifest 以保持与系统其余部分解析一致,
|
|
153
|
+
返回 model_dump() 字典以匹配 project_config 的 dict 消费约定。
|
|
154
|
+
|
|
155
|
+
Args:
|
|
156
|
+
manifest_path: 清单文件绝对路径
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
清单配置字典;解析失败时返回 None(由调用方决定如何提示)
|
|
160
|
+
"""
|
|
161
|
+
try:
|
|
162
|
+
# 延迟导入:避免在未使用时引入 shared.core 的依赖链。
|
|
163
|
+
# 注意 manifest 为 namespace package,需直接从 reader 子模块导入。
|
|
164
|
+
from app.shared.core.project.manifest.reader import load_manifest
|
|
165
|
+
|
|
166
|
+
manifest = load_manifest(manifest_path)
|
|
167
|
+
return manifest.model_dump(exclude_none=True)
|
|
168
|
+
except Exception as e:
|
|
169
|
+
# 记录日志但不抛出,避免加载失败阻断项目切换
|
|
170
|
+
logger.warning("加载项目清单失败: %s", e, exc_info=True)
|
|
171
|
+
return None
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
def resolve_project_label(project_path: str) -> str:
|
|
175
|
+
"""解析项目用于菜单显示的名称。
|
|
176
|
+
|
|
177
|
+
优先读取 project.precis.yaml 中的 project.name,失败则降级为目录名。
|
|
178
|
+
用于交互菜单 label,不做完整项目加载。
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
project_path: 项目根目录绝对路径
|
|
182
|
+
|
|
183
|
+
Returns:
|
|
184
|
+
项目显示名称
|
|
185
|
+
"""
|
|
186
|
+
manifest_path = find_manifest(project_path)
|
|
187
|
+
if manifest_path is not None:
|
|
188
|
+
config = load_manifest_config(manifest_path)
|
|
189
|
+
if config is not None:
|
|
190
|
+
name = config.get("project", {}).get("name")
|
|
191
|
+
if name:
|
|
192
|
+
return str(name)
|
|
193
|
+
# 降级:目录名
|
|
194
|
+
return os.path.basename(project_path) or project_path
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
@dataclass
|
|
198
|
+
class OpenResult:
|
|
199
|
+
"""打开项目纯逻辑的结果。
|
|
200
|
+
|
|
201
|
+
Attributes:
|
|
202
|
+
success: 是否成功打开
|
|
203
|
+
project_path: 项目绝对路径
|
|
204
|
+
config: 清单配置字典(无清单或加载失败时为 None)
|
|
205
|
+
manifest_path: 清单文件绝对路径;未找到时为 None
|
|
206
|
+
message: 给用户的消息文案(与原 CLI 文案保持一致,保证零回归)
|
|
207
|
+
"""
|
|
208
|
+
|
|
209
|
+
success: bool
|
|
210
|
+
project_path: str
|
|
211
|
+
config: dict | None
|
|
212
|
+
manifest_path: str | None
|
|
213
|
+
message: str
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def open_project(project_path: str) -> OpenResult:
|
|
217
|
+
"""打开项目的纯逻辑(存在性校验 + 写历史 + 加载清单)。
|
|
218
|
+
|
|
219
|
+
不修改任何 UI 上下文,仅返回 OpenResult 由调用方(CLI/TUI)自行更新各自上下文。
|
|
220
|
+
消息文案与原 open.py 保持完全一致,确保 CLI 零回归。
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
project_path: 项目目录的绝对路径
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
OpenResult:包含成功标志、配置、清单路径与消息文案
|
|
227
|
+
"""
|
|
228
|
+
if not os.path.exists(project_path):
|
|
229
|
+
return OpenResult(
|
|
230
|
+
success=False,
|
|
231
|
+
project_path=project_path,
|
|
232
|
+
config=None,
|
|
233
|
+
manifest_path=None,
|
|
234
|
+
message=f"项目路径不存在: {project_path}",
|
|
235
|
+
)
|
|
236
|
+
if not os.path.isdir(project_path):
|
|
237
|
+
return OpenResult(
|
|
238
|
+
success=False,
|
|
239
|
+
project_path=project_path,
|
|
240
|
+
config=None,
|
|
241
|
+
manifest_path=None,
|
|
242
|
+
message=f"路径不是目录: {project_path}",
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
# 写入历史记录
|
|
246
|
+
add_to_history(project_path)
|
|
247
|
+
|
|
248
|
+
# 定位清单文件,兼容 .yaml / .yml 两种后缀
|
|
249
|
+
manifest_path = find_manifest(project_path)
|
|
250
|
+
|
|
251
|
+
# 加载清单,加载失败不阻断切换项目,仅清空配置并给出警告
|
|
252
|
+
config: dict | None = None
|
|
253
|
+
load_warning = ""
|
|
254
|
+
if manifest_path is not None:
|
|
255
|
+
loaded = load_manifest_config(manifest_path)
|
|
256
|
+
if loaded is not None:
|
|
257
|
+
config = loaded
|
|
258
|
+
else:
|
|
259
|
+
config = None
|
|
260
|
+
load_warning = "\n警告:project.precis.yaml 存在但解析失败,配置未加载"
|
|
261
|
+
|
|
262
|
+
msg = f"已切换到项目: {project_path}"
|
|
263
|
+
if manifest_path is not None:
|
|
264
|
+
msg += "\n检测到项目清单文件 (project.precis.yaml)"
|
|
265
|
+
else:
|
|
266
|
+
msg += "\n警告:未找到 project.precis.yaml,可能需要初始化项目"
|
|
267
|
+
msg += load_warning
|
|
268
|
+
|
|
269
|
+
return OpenResult(
|
|
270
|
+
success=True,
|
|
271
|
+
project_path=project_path,
|
|
272
|
+
config=config,
|
|
273
|
+
manifest_path=manifest_path,
|
|
274
|
+
message=msg,
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
__all__ = [
|
|
279
|
+
"HISTORY_FILE",
|
|
280
|
+
"MAX_HISTORY",
|
|
281
|
+
"OpenResult",
|
|
282
|
+
"add_to_history",
|
|
283
|
+
"find_manifest",
|
|
284
|
+
"load_history",
|
|
285
|
+
"load_manifest_config",
|
|
286
|
+
"open_project",
|
|
287
|
+
"resolve_project_label",
|
|
288
|
+
]
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2026 Precis Team
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
# backend/app/cli/shell/__init__.py
|
|
17
|
+
"""
|
|
18
|
+
@fileoverview CLI Shell 模块入口
|
|
19
|
+
|
|
20
|
+
功能概述:
|
|
21
|
+
- 聚合导出 CLI Shell 核心组件(异常、格式化器)
|
|
22
|
+
- 提供统一的模块外部接口
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from app.cli.shell.exceptions import (
|
|
26
|
+
CLIError,
|
|
27
|
+
CommandNotFoundError,
|
|
28
|
+
ConfigError,
|
|
29
|
+
EditorError,
|
|
30
|
+
InvalidProjectError,
|
|
31
|
+
NoProjectOpenError,
|
|
32
|
+
ProjectNotFoundError,
|
|
33
|
+
ValidationError,
|
|
34
|
+
)
|
|
35
|
+
from app.cli.shell.formatter import Colors, Formatter
|
|
36
|
+
|
|
37
|
+
__all__ = [
|
|
38
|
+
"CLIError",
|
|
39
|
+
"ProjectNotFoundError",
|
|
40
|
+
"InvalidProjectError",
|
|
41
|
+
"NoProjectOpenError",
|
|
42
|
+
"CommandNotFoundError",
|
|
43
|
+
"ValidationError",
|
|
44
|
+
"ConfigError",
|
|
45
|
+
"EditorError",
|
|
46
|
+
"Formatter",
|
|
47
|
+
"Colors",
|
|
48
|
+
]
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
2
|
+
#
|
|
3
|
+
# Copyright 2026 Precis Team
|
|
4
|
+
#
|
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
# you may not use this file except in compliance with the License.
|
|
7
|
+
# You may obtain a copy of the License at
|
|
8
|
+
#
|
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
#
|
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
# See the License for the specific language governing permissions and
|
|
15
|
+
# limitations under the License.
|
|
16
|
+
# backend/app/cli/shell/commands/__init__.py
|
|
17
|
+
"""
|
|
18
|
+
@fileoverview CLI Shell 命令集合入口
|
|
19
|
+
|
|
20
|
+
功能概述:
|
|
21
|
+
- 聚合导出所有内置 Shell 命令类
|
|
22
|
+
- 统一暴露命令基类与结果类型
|
|
23
|
+
- 作为外部模块导入 commands 包时的统一入口
|
|
24
|
+
|
|
25
|
+
架构设计:
|
|
26
|
+
- 从各子模块导入具体的命令类
|
|
27
|
+
- 通过 __all__ 显式控制对外暴露的接口
|
|
28
|
+
- 遵循 Python 包导入规范,避免循环依赖
|
|
29
|
+
|
|
30
|
+
输入示例:
|
|
31
|
+
from app.cli.shell.commands import HelpCommand, OpenCommand
|
|
32
|
+
|
|
33
|
+
输出示例:
|
|
34
|
+
导入后可直接使用 HelpCommand() 等命令类
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
from app.cli.shell.commands.ai import AICommand
|
|
38
|
+
from app.cli.shell.commands.base import Command, CommandResult
|
|
39
|
+
from app.cli.shell.commands.config import ConfigCommand
|
|
40
|
+
from app.cli.shell.commands.exit import ExitCommand
|
|
41
|
+
from app.cli.shell.commands.help import HelpCommand
|
|
42
|
+
from app.cli.shell.commands.infer_schema import InferSchemaCommand
|
|
43
|
+
from app.cli.shell.commands.open import OpenCommand
|
|
44
|
+
from app.cli.shell.commands.project import ProjectCommand
|
|
45
|
+
from app.cli.shell.commands.provider import ProviderCommand
|
|
46
|
+
from app.cli.shell.commands.system import LsCommand, PwdCommand
|
|
47
|
+
from app.cli.shell.commands.validate import ValidateCommand
|
|
48
|
+
|
|
49
|
+
__all__ = [
|
|
50
|
+
"Command",
|
|
51
|
+
"CommandResult",
|
|
52
|
+
"HelpCommand",
|
|
53
|
+
"OpenCommand",
|
|
54
|
+
"ProjectCommand",
|
|
55
|
+
"ValidateCommand",
|
|
56
|
+
"ConfigCommand",
|
|
57
|
+
"ExitCommand",
|
|
58
|
+
"ProviderCommand",
|
|
59
|
+
"AICommand",
|
|
60
|
+
"PwdCommand",
|
|
61
|
+
"LsCommand",
|
|
62
|
+
"InferSchemaCommand",
|
|
63
|
+
]
|