testmcpy 0.9.0__tar.gz → 0.9.2__tar.gz
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.
- {testmcpy-0.9.0/testmcpy.egg-info → testmcpy-0.9.2}/PKG-INFO +8 -1
- {testmcpy-0.9.0 → testmcpy-0.9.2}/README.md +7 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/pyproject.toml +1 -1
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/api.py +122 -7
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/src/llm_integration.py +5 -5
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/storage.py +41 -15
- {testmcpy-0.9.0 → testmcpy-0.9.2/testmcpy.egg-info}/PKG-INFO +8 -1
- {testmcpy-0.9.0 → testmcpy-0.9.2}/LICENSE +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/MANIFEST.in +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/NOTICE +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/setup.cfg +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/__init__.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/__main__.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/agent/__init__.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/agent/hooks.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/agent/models.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/agent/orchestrator.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/agent/prompts.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/agent/tools.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/analytics.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/auth_debugger.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/auth_flow_recorder.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/cli/__init__.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/cli/app.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/cli/commands/__init__.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/cli/commands/agent.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/cli/commands/analytics.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/cli/commands/badge.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/cli/commands/baseline.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/cli/commands/bench.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/cli/commands/conformance.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/cli/commands/export_db.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/cli/commands/mcp.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/cli/commands/metamorphic.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/cli/commands/multi_env.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/cli/commands/mutate.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/cli/commands/push.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/cli/commands/run.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/cli/commands/scan.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/cli/commands/score.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/cli/commands/server.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/cli/commands/tools.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/cli/commands/tui.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/cli/commands/wizard.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/config.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/core/__init__.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/core/chat_session.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/core/docs_optimizer.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/core/mcp_manager.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/core/tool_comparison.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/core/tool_discovery.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/db.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/error_handlers.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/evals/__init__.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/evals/auth_evaluators.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/evals/base_evaluators.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/evals/evaluator_packs.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/evals/security_evaluators.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/formatters/__init__.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/formatters/base.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/formatters/curl.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/formatters/graphql.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/formatters/javascript_client.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/formatters/json_yaml.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/formatters/protobuf.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/formatters/python.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/formatters/python_client.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/formatters/thrift.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/formatters/typescript.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/formatters/typescript_client.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/llm_profiles.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/mcp_profiles.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/migrate_json.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/models.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/research/claude_sdk_detailed_exploration.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/research/claude_sdk_poc.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/research/claude_sdk_working_poc.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/research/test_ollama_tools.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/security/__init__.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/security/rules.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/security/scanner.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/__init__.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/auth_middleware.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/helpers/__init__.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/helpers/mcp_config.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/models.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/routers/__init__.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/routers/agent.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/routers/analytics.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/routers/auth.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/routers/compare.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/routers/compatibility.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/routers/generation_logs.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/routers/health.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/routers/llm.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/routers/mcp_profiles.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/routers/metrics.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/routers/results.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/routers/runs.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/routers/search.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/routers/security.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/routers/smoke_reports.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/routers/test_profiles.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/routers/tests.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/routers/tools.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/run_persistence.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/run_registry.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/state.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/server/websocket.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/smoke_test.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/src/__init__.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/src/baseline.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/src/ci_gate.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/src/comparison_runner.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/src/coverage_analyzer.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/src/emitters.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/src/html_report.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/src/mcp_client.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/src/metamorphic.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/src/model_registry.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/src/models.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/src/multi_env.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/src/oauth_flows.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/src/prompt_mutation.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/src/report_generator.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/src/runner_tools.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/src/schema_diff.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/src/test_runner.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/src/token_manager.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/src/usability_score.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/test_profiles.py +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/README.md +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/dist/assets/index-BXP9_Odn.js +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/dist/assets/index-D35cfDhp.css +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/dist/index.html +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/index.html +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/package-lock.json +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/package.json +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/postcss.config.js +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/App.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/BackgroundRunsIndicator.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/Badge.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/CommandPalette.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/CompareToolsTab.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/ConfirmDialog.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/EditorStatusBar.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/EditorTabStrip.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/ErrorAlert.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/ErrorBoundary.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/LLMProfileSelector.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/LoadingSpinner.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/MCPProfileSelector.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/NotificationProvider.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/OptimizeDocsModal.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/OutputDiff.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/ParameterCard.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/SchemaCodeViewer.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/SkeletonLoader.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/StreamingLogViewer.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/TestGenerationModal.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/TestProfileSelector.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/TestResultPanel.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/TestStatusIndicator.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/ToolCallTimeline.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/ToolComparison.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/ToolDebugModal.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/TraceView.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/TypeBadge.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/Wizard.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/components/__tests__/OutputDiff.test.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/contexts/TestRunContext.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/contexts/ThemeContext.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/hooks/useEditorTheme.js +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/hooks/useKeyboardShortcuts.js +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/hooks/useSafeFetch.js +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/index.css +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/main.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/pages/AuthDebugger.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/pages/ChatInterface.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/pages/Configuration.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/pages/GenerationHistory.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/pages/LLMProfiles.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/pages/MCPExplorer.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/pages/MCPProfiles.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/pages/Performance.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/pages/ProfilesManager.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/pages/Reports.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/pages/SecurityDashboard.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/pages/Servers.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/pages/TestManager.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/pages/__tests__/ChatInterface.test.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/pages/__tests__/Performance.test.jsx +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/test-setup.js +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/utils/__tests__/formatConverters.test.js +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/utils/formatConverters.js +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/src/utils/formatters.js +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/tailwind.config.js +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/vite.config.js +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy/ui/vitest.config.js +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy.egg-info/SOURCES.txt +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy.egg-info/dependency_links.txt +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy.egg-info/entry_points.txt +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy.egg-info/requires.txt +0 -0
- {testmcpy-0.9.0 → testmcpy-0.9.2}/testmcpy.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: testmcpy
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.2
|
|
4
4
|
Summary: A comprehensive testing framework for validating LLM tool calling capabilities with MCP services
|
|
5
5
|
Author: Amin Ghadersohi
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -351,6 +351,13 @@ profiles:
|
|
|
351
351
|
|
|
352
352
|
The setup command is **idempotent** — safe to run multiple times. Use `--force` to overwrite existing files.
|
|
353
353
|
|
|
354
|
+
**`TESTMCPY_CHAT_OAUTH_LOGIN`** (default `true`): when a chat message hits an
|
|
355
|
+
OAuth (`oauth_auto_discover`) MCP profile with no cached token, the server opens
|
|
356
|
+
the interactive browser OAuth flow and retries. This assumes a browser is
|
|
357
|
+
available on the machine running the server — in headless deployments set
|
|
358
|
+
`TESTMCPY_CHAT_OAUTH_LOGIN=false` so the request fails fast with a clear error
|
|
359
|
+
instead of blocking on a login that can never complete.
|
|
360
|
+
|
|
354
361
|
### 2. Explore Your MCP Service
|
|
355
362
|
|
|
356
363
|
```bash
|
|
@@ -271,6 +271,13 @@ profiles:
|
|
|
271
271
|
|
|
272
272
|
The setup command is **idempotent** — safe to run multiple times. Use `--force` to overwrite existing files.
|
|
273
273
|
|
|
274
|
+
**`TESTMCPY_CHAT_OAUTH_LOGIN`** (default `true`): when a chat message hits an
|
|
275
|
+
OAuth (`oauth_auto_discover`) MCP profile with no cached token, the server opens
|
|
276
|
+
the interactive browser OAuth flow and retries. This assumes a browser is
|
|
277
|
+
available on the machine running the server — in headless deployments set
|
|
278
|
+
`TESTMCPY_CHAT_OAUTH_LOGIN=false` so the request fails fast with a clear error
|
|
279
|
+
instead of blocking on a login that can never complete.
|
|
280
|
+
|
|
274
281
|
### 2. Explore Your MCP Service
|
|
275
282
|
|
|
276
283
|
```bash
|
|
@@ -93,7 +93,7 @@ testmcpy = [
|
|
|
93
93
|
|
|
94
94
|
[project]
|
|
95
95
|
name = "testmcpy"
|
|
96
|
-
version = "0.9.
|
|
96
|
+
version = "0.9.2"
|
|
97
97
|
description = "A comprehensive testing framework for validating LLM tool calling capabilities with MCP services"
|
|
98
98
|
authors = [{name = "Amin Ghadersohi"}]
|
|
99
99
|
license = "Apache-2.0"
|
|
@@ -151,6 +151,22 @@ def _get_init_lock(cache_key: str) -> asyncio.Lock:
|
|
|
151
151
|
return _client_init_locks[cache_key]
|
|
152
152
|
|
|
153
153
|
|
|
154
|
+
def _primary_mcp_provider_kwargs(
|
|
155
|
+
clients_to_use: list[tuple[str, str, MCPClient]],
|
|
156
|
+
) -> dict[str, Any]:
|
|
157
|
+
"""mcp_url/auth kwargs from the FIRST selected MCP client.
|
|
158
|
+
|
|
159
|
+
SDK providers support a single MCP server; the Chat UI sends exactly one
|
|
160
|
+
"profileId:mcpName". Without these kwargs the providers fall back to the
|
|
161
|
+
DEFAULT profile's URL/auth, breaking chat for any other selected profile.
|
|
162
|
+
create_llm_provider filters these out for providers that don't accept them.
|
|
163
|
+
"""
|
|
164
|
+
if not clients_to_use:
|
|
165
|
+
return {}
|
|
166
|
+
_profile_id, _mcp_name, client = clients_to_use[0]
|
|
167
|
+
return {"mcp_url": client.base_url, "auth": client.auth_config}
|
|
168
|
+
|
|
169
|
+
|
|
154
170
|
async def get_mcp_clients_for_profile(profile_id: str) -> list[tuple[str, MCPClient]]:
|
|
155
171
|
"""
|
|
156
172
|
Get or create MCP clients for all MCP servers in a profile.
|
|
@@ -299,12 +315,15 @@ async def get_mcp_client_for_server(profile_id: str, mcp_name: str) -> MCPClient
|
|
|
299
315
|
return client
|
|
300
316
|
|
|
301
317
|
|
|
302
|
-
async def clear_cached_client(cache_key: str) -> bool:
|
|
318
|
+
async def clear_cached_client(cache_key: str, record_failure: bool = True) -> bool:
|
|
303
319
|
"""
|
|
304
320
|
Clear a cached MCP client by its cache key.
|
|
305
321
|
|
|
306
322
|
Args:
|
|
307
323
|
cache_key: Cache key in format "{profile_id}:{mcp_name}"
|
|
324
|
+
record_failure: When True (default), throttle the next reconnect via
|
|
325
|
+
back-off. Pass False for deliberate re-initialization (e.g. an
|
|
326
|
+
interactive OAuth re-login) where an immediate reconnect is wanted.
|
|
308
327
|
|
|
309
328
|
Returns:
|
|
310
329
|
True if a client was cleared, False if no client was cached
|
|
@@ -313,8 +332,9 @@ async def clear_cached_client(cache_key: str) -> bool:
|
|
|
313
332
|
|
|
314
333
|
client = mcp_clients.pop(cache_key, None)
|
|
315
334
|
if client:
|
|
316
|
-
|
|
317
|
-
|
|
335
|
+
if record_failure:
|
|
336
|
+
# Record a failure so the next reconnect is throttled via back-off.
|
|
337
|
+
_record_failure(cache_key)
|
|
318
338
|
try:
|
|
319
339
|
await client.close()
|
|
320
340
|
print(f"Cleared cached client '{cache_key}'")
|
|
@@ -324,6 +344,69 @@ async def clear_cached_client(cache_key: str) -> bool:
|
|
|
324
344
|
return False
|
|
325
345
|
|
|
326
346
|
|
|
347
|
+
# Marker substring of the ValueError raised by BaseSDKProvider when an
|
|
348
|
+
# oauth_auto_discover profile has no cached token (see
|
|
349
|
+
# llm_integration.BaseSDKProvider._resolve_mcp_bearer_token).
|
|
350
|
+
_OAUTH_TOKEN_ERROR = "No usable cached OAuth token"
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
def _chat_oauth_login_enabled() -> bool:
|
|
354
|
+
"""Feature flag for interactive OAuth login during chat (default ON).
|
|
355
|
+
|
|
356
|
+
Disable with TESTMCPY_CHAT_OAUTH_LOGIN=false (or 0/no). Read at call time
|
|
357
|
+
so tests can monkeypatch the environment.
|
|
358
|
+
"""
|
|
359
|
+
return os.environ.get("TESTMCPY_CHAT_OAUTH_LOGIN", "true").strip().lower() not in (
|
|
360
|
+
"0",
|
|
361
|
+
"false",
|
|
362
|
+
"no",
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
async def _relogin_oauth_servers(server_keys: list[str]) -> dict[str, MCPClient]:
|
|
367
|
+
"""Deliberate interactive re-auth for the given "profileId:mcpName" keys.
|
|
368
|
+
|
|
369
|
+
Drops cached clients WITHOUT recording back-off, clears any pre-existing
|
|
370
|
+
back-off state, and re-initializes. MCPClient.initialize() with
|
|
371
|
+
oauth_auto_discover opens the browser OAuth flow and caches the token via
|
|
372
|
+
fastmcp FileTokenStorage; duplicate popups are prevented by the per-key
|
|
373
|
+
init locks.
|
|
374
|
+
|
|
375
|
+
Returns the fresh clients keyed by cache key so callers can replace any
|
|
376
|
+
references to the old, now-closed client objects.
|
|
377
|
+
"""
|
|
378
|
+
new_clients: dict[str, MCPClient] = {}
|
|
379
|
+
for cache_key in server_keys:
|
|
380
|
+
await clear_cached_client(cache_key, record_failure=False)
|
|
381
|
+
_clear_failure(cache_key) # earlier failures must not block deliberate re-auth
|
|
382
|
+
profile_id, mcp_name = cache_key.split(":", 1)
|
|
383
|
+
client = await get_mcp_client_for_server(profile_id, mcp_name)
|
|
384
|
+
if client:
|
|
385
|
+
new_clients[cache_key] = client
|
|
386
|
+
return new_clients
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def _refresh_client_refs(
|
|
390
|
+
new_clients: dict[str, MCPClient],
|
|
391
|
+
clients_to_use: list[tuple[str, str, MCPClient]],
|
|
392
|
+
tool_to_client: dict[str, tuple[MCPClient, str, str]],
|
|
393
|
+
) -> tuple[list[tuple[str, str, MCPClient]], dict[str, tuple[MCPClient, str, str]]]:
|
|
394
|
+
"""Swap re-logged-in clients into the chat endpoints' lookup structures.
|
|
395
|
+
|
|
396
|
+
After _relogin_oauth_servers the old client objects are closed; tool
|
|
397
|
+
execution through tool_to_client must use the replacements.
|
|
398
|
+
"""
|
|
399
|
+
refreshed_clients = [
|
|
400
|
+
(pid, name, new_clients.get(f"{pid}:{name}", client))
|
|
401
|
+
for pid, name, client in clients_to_use
|
|
402
|
+
]
|
|
403
|
+
refreshed_tools = {
|
|
404
|
+
tool: (new_clients.get(f"{pid}:{name}", client), pid, name)
|
|
405
|
+
for tool, (client, pid, name) in tool_to_client.items()
|
|
406
|
+
}
|
|
407
|
+
return refreshed_clients, refreshed_tools
|
|
408
|
+
|
|
409
|
+
|
|
327
410
|
def is_auth_error(error_msg: str) -> bool:
|
|
328
411
|
"""Check if an error message indicates an authentication failure."""
|
|
329
412
|
error_lower = error_msg.lower()
|
|
@@ -936,9 +1019,25 @@ async def chat(request: ChatRequest) -> ChatResponse:
|
|
|
936
1019
|
provider_kwargs = {}
|
|
937
1020
|
if api_key:
|
|
938
1021
|
provider_kwargs["api_key"] = api_key
|
|
939
|
-
|
|
1022
|
+
provider_kwargs.update(_primary_mcp_provider_kwargs(clients_to_use))
|
|
940
1023
|
print("[Chat] Initializing LLM provider...")
|
|
941
|
-
|
|
1024
|
+
try:
|
|
1025
|
+
llm_provider = create_llm_provider(provider, model, **provider_kwargs)
|
|
1026
|
+
await llm_provider.initialize()
|
|
1027
|
+
except ValueError as e:
|
|
1028
|
+
if not (_chat_oauth_login_enabled() and _OAUTH_TOKEN_ERROR in str(e)):
|
|
1029
|
+
raise
|
|
1030
|
+
print("[Chat] No cached OAuth token; triggering interactive OAuth login...")
|
|
1031
|
+
new_clients = await _relogin_oauth_servers(accessed_servers)
|
|
1032
|
+
# The old client objects are closed now — swap in the replacements
|
|
1033
|
+
# so tool execution doesn't hit a closed client.
|
|
1034
|
+
clients_to_use, tool_to_client = _refresh_client_refs(
|
|
1035
|
+
new_clients, clients_to_use, tool_to_client
|
|
1036
|
+
)
|
|
1037
|
+
provider_kwargs.update(_primary_mcp_provider_kwargs(clients_to_use))
|
|
1038
|
+
llm_provider = create_llm_provider(provider, model, **provider_kwargs)
|
|
1039
|
+
# Single retry; a second failure falls to the existing handlers.
|
|
1040
|
+
await llm_provider.initialize()
|
|
942
1041
|
print(
|
|
943
1042
|
f"[Chat] LLM provider initialized. Generating response with {len(all_tools)} tools..."
|
|
944
1043
|
)
|
|
@@ -1209,8 +1308,24 @@ async def chat_stream(request: ChatRequest):
|
|
|
1209
1308
|
provider_kwargs: dict = {}
|
|
1210
1309
|
if api_key:
|
|
1211
1310
|
provider_kwargs["api_key"] = api_key
|
|
1212
|
-
|
|
1213
|
-
|
|
1311
|
+
provider_kwargs.update(_primary_mcp_provider_kwargs(clients_to_use))
|
|
1312
|
+
try:
|
|
1313
|
+
llm_provider = create_llm_provider(provider, model, **provider_kwargs)
|
|
1314
|
+
await llm_provider.initialize()
|
|
1315
|
+
except ValueError as e:
|
|
1316
|
+
if not (_chat_oauth_login_enabled() and _OAUTH_TOKEN_ERROR in str(e)):
|
|
1317
|
+
raise
|
|
1318
|
+
yield send_event("status", "Waiting for OAuth login in browser...")
|
|
1319
|
+
new_clients = await _relogin_oauth_servers(accessed_servers)
|
|
1320
|
+
# The old client objects are closed now — swap in the replacements
|
|
1321
|
+
# so tool execution doesn't hit a closed client.
|
|
1322
|
+
clients_to_use, tool_to_client = _refresh_client_refs(
|
|
1323
|
+
new_clients, clients_to_use, tool_to_client
|
|
1324
|
+
)
|
|
1325
|
+
provider_kwargs.update(_primary_mcp_provider_kwargs(clients_to_use))
|
|
1326
|
+
llm_provider = create_llm_provider(provider, model, **provider_kwargs)
|
|
1327
|
+
# Single retry; a second failure falls to the existing handlers.
|
|
1328
|
+
await llm_provider.initialize()
|
|
1214
1329
|
|
|
1215
1330
|
# --- Detect if provider is SDK-based (handles its own agentic loop) ---
|
|
1216
1331
|
from testmcpy.src.llm_integration import ClaudeSDKProvider
|
|
@@ -930,6 +930,7 @@ class AnthropicProvider(LLMProvider):
|
|
|
930
930
|
api_key: str | None = None,
|
|
931
931
|
base_url: str = "https://api.anthropic.com",
|
|
932
932
|
mcp_url: str | None = None,
|
|
933
|
+
auth: dict[str, Any] | None = None,
|
|
933
934
|
):
|
|
934
935
|
self.model = model
|
|
935
936
|
# Use config system for API key
|
|
@@ -940,11 +941,10 @@ class AnthropicProvider(LLMProvider):
|
|
|
940
941
|
# Use MCP_URL and auth from default profile if not provided
|
|
941
942
|
if mcp_url is None:
|
|
942
943
|
mcp_url = config.get_mcp_url()
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
auth = default_mcp.auth.to_dict()
|
|
944
|
+
if auth is None:
|
|
945
|
+
default_mcp = config.get_default_mcp_server()
|
|
946
|
+
if default_mcp and default_mcp.auth:
|
|
947
|
+
auth = default_mcp.auth.to_dict()
|
|
948
948
|
self.tool_discovery = ToolDiscoveryService(mcp_url, auth=auth)
|
|
949
949
|
|
|
950
950
|
async def initialize(self):
|
|
@@ -138,21 +138,38 @@ class TestStorage:
|
|
|
138
138
|
# a manual `alembic upgrade` on existing DB files.
|
|
139
139
|
self._apply_column_migrations()
|
|
140
140
|
|
|
141
|
+
@staticmethod
|
|
142
|
+
def _sqlite_default_literal(column) -> str | None:
|
|
143
|
+
"""SQLite DDL DEFAULT literal for a mapped column's scalar default.
|
|
144
|
+
|
|
145
|
+
Only plain scalar defaults are rendered (bool/int/float/str) — callables
|
|
146
|
+
and server-side expressions return None so the column is added without
|
|
147
|
+
a DEFAULT (NULL for existing rows, Python-side default for new ones).
|
|
148
|
+
"""
|
|
149
|
+
default = column.default
|
|
150
|
+
if default is None or not getattr(default, "is_scalar", False):
|
|
151
|
+
return None
|
|
152
|
+
value = default.arg
|
|
153
|
+
if isinstance(value, bool):
|
|
154
|
+
return str(int(value))
|
|
155
|
+
if isinstance(value, (int, float)):
|
|
156
|
+
return str(value)
|
|
157
|
+
if isinstance(value, str):
|
|
158
|
+
escaped = value.replace("'", "''")
|
|
159
|
+
return f"'{escaped}'"
|
|
160
|
+
return None
|
|
161
|
+
|
|
141
162
|
def _apply_column_migrations(self) -> None:
|
|
142
163
|
"""Idempotently add columns that were introduced after the initial schema.
|
|
143
164
|
|
|
144
165
|
create_all() creates missing *tables* but never alters existing ones, so
|
|
145
166
|
users who created their DB before a new column was added will hit
|
|
146
|
-
OperationalError on INSERT.
|
|
147
|
-
|
|
167
|
+
OperationalError on SELECT/INSERT. Missing columns are derived from the
|
|
168
|
+
ORM metadata (every mapped column not present in the live table) rather
|
|
169
|
+
than a hand-maintained list — the previous list drifted from the models
|
|
170
|
+
more than once (e.g. ``manual_false_positive`` was missed, breaking
|
|
171
|
+
``GET /api/results/run/{id}`` on older DB files with a 500).
|
|
148
172
|
"""
|
|
149
|
-
# (table, column, DDL type)
|
|
150
|
-
migrations = [
|
|
151
|
-
("question_results", "tool_call_counts", "JSON"),
|
|
152
|
-
("question_results", "false_positive_rate", "FLOAT DEFAULT 0.0"),
|
|
153
|
-
("test_runs", "total_cost", "FLOAT DEFAULT 0.0"),
|
|
154
|
-
("test_runs", "heartbeat_at", "VARCHAR"),
|
|
155
|
-
]
|
|
156
173
|
# (index name, table, columns) — indexes added after the initial
|
|
157
174
|
# schema; create_all skips existing tables so these need explicit DDL
|
|
158
175
|
index_migrations = [
|
|
@@ -172,14 +189,23 @@ class TestStorage:
|
|
|
172
189
|
if is_sqlite:
|
|
173
190
|
# PRAGMA is SQLite-only; non-SQLite backends are expected
|
|
174
191
|
# to be managed with `alembic upgrade head`.
|
|
175
|
-
for table
|
|
176
|
-
result = conn.execute(text(f"PRAGMA table_info({table})"))
|
|
192
|
+
for table in Base.metadata.sorted_tables:
|
|
193
|
+
result = conn.execute(text(f"PRAGMA table_info({table.name})"))
|
|
177
194
|
existing = {row[1] for row in result}
|
|
178
|
-
if
|
|
179
|
-
|
|
180
|
-
|
|
195
|
+
if not existing:
|
|
196
|
+
continue # create_all just made it; nothing to alter
|
|
197
|
+
for column in table.columns:
|
|
198
|
+
if column.name in existing:
|
|
199
|
+
continue
|
|
200
|
+
ddl_type = column.type.compile(self._engine.dialect)
|
|
201
|
+
ddl = f"ALTER TABLE {table.name} ADD COLUMN {column.name} {ddl_type}"
|
|
202
|
+
default = self._sqlite_default_literal(column)
|
|
203
|
+
if default is not None:
|
|
204
|
+
ddl += f" DEFAULT {default}"
|
|
205
|
+
conn.execute(text(ddl))
|
|
206
|
+
for index_name, table_name, columns in index_migrations:
|
|
181
207
|
conn.execute(
|
|
182
|
-
text(f"CREATE INDEX IF NOT EXISTS {index_name} ON {
|
|
208
|
+
text(f"CREATE INDEX IF NOT EXISTS {index_name} ON {table_name} ({columns})")
|
|
183
209
|
)
|
|
184
210
|
conn.commit()
|
|
185
211
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: testmcpy
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.2
|
|
4
4
|
Summary: A comprehensive testing framework for validating LLM tool calling capabilities with MCP services
|
|
5
5
|
Author: Amin Ghadersohi
|
|
6
6
|
License-Expression: Apache-2.0
|
|
@@ -351,6 +351,13 @@ profiles:
|
|
|
351
351
|
|
|
352
352
|
The setup command is **idempotent** — safe to run multiple times. Use `--force` to overwrite existing files.
|
|
353
353
|
|
|
354
|
+
**`TESTMCPY_CHAT_OAUTH_LOGIN`** (default `true`): when a chat message hits an
|
|
355
|
+
OAuth (`oauth_auto_discover`) MCP profile with no cached token, the server opens
|
|
356
|
+
the interactive browser OAuth flow and retries. This assumes a browser is
|
|
357
|
+
available on the machine running the server — in headless deployments set
|
|
358
|
+
`TESTMCPY_CHAT_OAUTH_LOGIN=false` so the request fails fast with a clear error
|
|
359
|
+
instead of blocking on a login that can never complete.
|
|
360
|
+
|
|
354
361
|
### 2. Explore Your MCP Service
|
|
355
362
|
|
|
356
363
|
```bash
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|