@atmem/architect 0.0.0-stage → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CONSTITUTION.md +144 -0
- package/LICENSE +202 -0
- package/NOTICE +6 -0
- package/README.md +31 -2
- package/agent-skills/discovery/SKILL.md +48 -0
- package/agent-skills/discovery/topics.json +139 -0
- package/agent-skills/generation-capabilities.json +760 -0
- package/agent-skills/service-lifecycle.json +21 -0
- package/agent-skills/specialists/architecture/SKILL.md +15 -0
- package/agent-skills/specialists/aws/SKILL.md +17 -0
- package/agent-skills/specialists/azure/SKILL.md +7 -0
- package/agent-skills/specialists/composition/SKILL.md +17 -0
- package/agent-skills/specialists/devops/SKILL.md +10 -0
- package/agent-skills/specialists/finops/SKILL.md +7 -0
- package/agent-skills/specialists/gcp/SKILL.md +7 -0
- package/agent-skills/specialists/ibm/SKILL.md +7 -0
- package/agent-skills/specialists/manifest.json +13 -0
- package/agent-skills/specialists/pulumi/SKILL.md +7 -0
- package/agent-skills/specialists/security/SKILL.md +7 -0
- package/agent-skills/specialists/terraform/SKILL.md +14 -0
- package/agent-skills/upstream/aws/LICENSE +175 -0
- package/agent-skills/upstream/aws/SKILL.md +125 -0
- package/agent-skills/upstream/azure/LICENSE +21 -0
- package/agent-skills/upstream/azure/SKILL.md +387 -0
- package/agent-skills/upstream/gcp/LICENSE +202 -0
- package/agent-skills/upstream/gcp/SKILL.md +159 -0
- package/agent-skills/upstream/manifest.json +57 -0
- package/agent-skills/upstream/pulumi/LICENSE +201 -0
- package/agent-skills/upstream/pulumi/SKILL.md +501 -0
- package/agent-skills/upstream/terraform/LICENSE +373 -0
- package/agent-skills/upstream/terraform/SKILL.md +316 -0
- package/agent-skills/workflows/workflows.json +601 -0
- package/cli/architect.mjs +3 -0
- package/cli/runtime.mjs +115 -0
- package/dist/assets/html2canvas.esm-QH1iLAAe.js +22 -0
- package/dist/assets/index-BTLHaa3J.css +1 -0
- package/dist/assets/index-CQ2C1gKF.js +21338 -0
- package/dist/assets/index.es-DTyqIdaO.js +18 -0
- package/dist/assets/purify.es-CAi2OJFm.js +3 -0
- package/dist/assets/visual-export-Ox0j5xj5.js +173 -0
- package/dist/favicon.svg +2 -0
- package/dist/fonts/DM-Sans-OFL.txt +93 -0
- package/dist/fonts/Manrope-OFL.txt +93 -0
- package/dist/fonts/NotoSans-Regular.ttf +0 -0
- package/dist/fonts/OFL.txt +94 -0
- package/dist/fonts/dm-sans-latin-wght-normal.woff2 +0 -0
- package/dist/fonts/manrope-latin-wght-normal.woff2 +0 -0
- package/dist/index.html +4 -0
- package/dist/licenses/atmem-APACHE-2.0.txt +202 -0
- package/dist/licenses/aws-architect-LICENSE.txt +17 -0
- package/dist/licenses/aws-mcp-LICENSE.txt +175 -0
- package/dist/licenses/aws-mcp-NOTICE.txt +1 -0
- package/dist/licenses/cloud-emulation-notices.txt +36619 -0
- package/dist/licenses/dependency-notices.txt +6379 -0
- package/dist/licenses/emulation-notices.txt +717 -0
- package/dist/licenses/github-copilot-sdk-LICENSE.txt +21 -0
- package/dist/licenses/memory-dependency-notices.txt +3319 -0
- package/dist/licenses/openarchflow-LICENSE.txt +21 -0
- package/dist/licenses/python-dependency-notices.txt +11951 -0
- package/docs/upstream-lock.json +26 -0
- package/knowledge/engineering-practices.json +536 -0
- package/package.json +30 -4
- package/packaging/uv-release-lock.json +54 -0
- package/public/licenses/atmem-APACHE-2.0.txt +202 -0
- package/public/licenses/aws-architect-LICENSE.txt +17 -0
- package/public/licenses/aws-mcp-LICENSE.txt +175 -0
- package/public/licenses/aws-mcp-NOTICE.txt +1 -0
- package/public/licenses/cloud-emulation-notices.txt +36619 -0
- package/public/licenses/dependency-notices.txt +6379 -0
- package/public/licenses/emulation-notices.txt +717 -0
- package/public/licenses/github-copilot-sdk-LICENSE.txt +21 -0
- package/public/licenses/memory-dependency-notices.txt +3319 -0
- package/public/licenses/openarchflow-LICENSE.txt +21 -0
- package/public/licenses/python-dependency-notices.txt +11951 -0
- package/scripts/terraform-render.mjs +8 -0
- package/server/__init__.py +0 -0
- package/server/abstraction.py +28 -0
- package/server/agent_runtime.py +153 -0
- package/server/app.py +257 -0
- package/server/architect.py +112 -0
- package/server/architecture_activity.py +67 -0
- package/server/attachments.py +101 -0
- package/server/cloud-emulation-requirements.txt +29 -0
- package/server/codex_app_server.py +219 -0
- package/server/codex_engineering_backend.py +266 -0
- package/server/codex_worker_policy.py +193 -0
- package/server/contracts.py +315 -0
- package/server/copilot_worker.py +16 -0
- package/server/dependency_mirror.py +54 -0
- package/server/design_patch.py +43 -0
- package/server/emulation-requirements.txt +8 -0
- package/server/emulation_adapters.py +20 -0
- package/server/emulation_routes.py +98 -0
- package/server/emulation_worker.py +351 -0
- package/server/engineering_artifacts.py +221 -0
- package/server/engineering_assets/application-runtime.json +4372 -0
- package/server/engineering_assets/architecture_work.schema.json +79 -0
- package/server/engineering_assets/materialize_runtime.py +62 -0
- package/server/engineering_assets/prepare_tools.py +96 -0
- package/server/engineering_assets/sql_validation.py +59 -0
- package/server/engineering_contexts.py +120 -0
- package/server/engineering_delivery.py +210 -0
- package/server/engineering_finalization.py +210 -0
- package/server/engineering_handoff.py +172 -0
- package/server/engineering_jobs.py +206 -0
- package/server/engineering_knowledge.py +75 -0
- package/server/engineering_reconciliation.py +159 -0
- package/server/engineering_routes.py +483 -0
- package/server/engineering_sessions.py +273 -0
- package/server/engineering_tools.py +166 -0
- package/server/generation_lint.py +18 -0
- package/server/local_providers.py +185 -0
- package/server/local_validation.py +236 -0
- package/server/memory-requirements.lock +181 -0
- package/server/memory-requirements.txt +6 -0
- package/server/memory_context.py +58 -0
- package/server/memory_routes.py +141 -0
- package/server/memory_service.py +306 -0
- package/server/memory_worker.py +129 -0
- package/server/openai_voice.py +80 -0
- package/server/orca_handoff.py +90 -0
- package/server/packaged_app.py +9 -0
- package/server/pricing.py +190 -0
- package/server/process_control_observation.py +31 -0
- package/server/prompts.py +136 -0
- package/server/public_faq_emulation_worker.py +44 -0
- package/server/requirements.lock +1618 -0
- package/server/requirements.txt +68 -0
- package/server/runtime_dependency_locks.py +27 -0
- package/server/runtime_evidence.py +93 -0
- package/server/settings.py +14 -0
- package/server/skills.py +103 -0
- package/server/storage_emulation_worker.py +160 -0
- package/server/structured_emulation_worker.py +48 -0
- package/server/terraform_routes.py +118 -0
- package/server/upstream/__init__.py +0 -0
- package/server/upstream/agent/__init__.py +1 -0
- package/server/upstream/agent/aws_architect.py +600 -0
- package/server/upstream/agent/config/__init__.py +1 -0
- package/server/upstream/agent/config/agent_config.py +82 -0
- package/server/upstream/agent/config/prompts.py +138 -0
- package/server/upstream/agent/rate_limiter.py +62 -0
- package/server/upstream/utils/__init__.py +1 -0
- package/server/upstream/utils/file_writer.py +43 -0
- package/server/validation_applicability.py +85 -0
- package/server/validation_jobs.py +252 -0
- package/server/validation_workers.py +48 -0
- package/server/voice_routes.py +91 -0
- package/server/voice_worker.py +26 -0
- package/server/windows_snapshot.py +119 -0
- package/specs/028-convergence-evaluation/evidence/20261110-ibm-invoice-native/registry-structured/qualification.json +72 -0
- package/specs/028-convergence-evaluation/evidence/20261110-invoice-runtime/durable-current/receipt.json +61 -0
- package/specs/029-general-purpose-engine/evidence/20261111-runtime-dependencies/sdk/receipt.json +22 -0
- package/src/ArchitectureActivity.jsx +54 -0
- package/src/ArchitectureWorkPanel.jsx +39 -0
- package/src/ArtifactChecks.jsx +49 -0
- package/src/BlockRequirements.jsx +13 -0
- package/src/BrainActivity.jsx +11 -0
- package/src/ChatMessage.jsx +29 -0
- package/src/ChatResizeHandle.jsx +6 -0
- package/src/CodePreview.jsx +8 -0
- package/src/ComponentReview.jsx +31 -0
- package/src/DeliveryViews.jsx +59 -0
- package/src/EmulationPanel.jsx +44 -0
- package/src/EngagementViews.jsx +387 -0
- package/src/EngineeringPanel.jsx +151 -0
- package/src/FileTree.jsx +7 -0
- package/src/FinalChecklist.jsx +15 -0
- package/src/HelpNote.jsx +3 -0
- package/src/ImplementationDelivery.jsx +91 -0
- package/src/JourneyViews.jsx +403 -0
- package/src/LocationChoices.jsx +12 -0
- package/src/MemoryPanel.jsx +58 -0
- package/src/OpenAIVoicePanel.jsx +66 -0
- package/src/OutputsView.jsx +71 -0
- package/src/ProjectChecks.jsx +44 -0
- package/src/TerraformView.jsx +57 -0
- package/src/WorkbenchDialogs.jsx +532 -0
- package/src/activity-presentation.mjs +28 -0
- package/src/ai-qualification-scenarios.mjs +31 -0
- package/src/approval-application-files.mjs +32 -0
- package/src/approval-applications.mjs +82 -0
- package/src/approval-spec-kit.mjs +30 -0
- package/src/architecture-activity.css +28 -0
- package/src/architecture-activity.mjs +63 -0
- package/src/architecture-work.mjs +176 -0
- package/src/artifact-work.mjs +103 -0
- package/src/async-documents.mjs +191 -0
- package/src/atflow.css +1140 -0
- package/src/atforma.css +748 -0
- package/src/atmem-tokens.css +79 -0
- package/src/aws-preflight.mjs +60 -0
- package/src/aws-public-faq.mjs +102 -0
- package/src/aws-validation-scenarios.mjs +10 -0
- package/src/aws-validation.mjs +48 -0
- package/src/azure-plan-mocks.mjs +25 -0
- package/src/azure-structured.mjs +133 -0
- package/src/bedrock-summary.mjs +152 -0
- package/src/bounded-rag.mjs +60 -0
- package/src/brand.css +9 -0
- package/src/canvas-view.mjs +28 -0
- package/src/capability-composition.mjs +44 -0
- package/src/chat-attachments.mjs +13 -0
- package/src/cloud-provider-locks.mjs +9 -0
- package/src/cloud-storage-scenarios.mjs +2 -0
- package/src/component-review.mjs +55 -0
- package/src/composition-spec-kit.mjs +30 -0
- package/src/consultation-context.mjs +15 -0
- package/src/consultation.mjs +52 -0
- package/src/container-contracts.mjs +5 -0
- package/src/container-inspection.mjs +75 -0
- package/src/container-runtime.mjs +30 -0
- package/src/container-security-inspection.mjs +49 -0
- package/src/container-terraform.mjs +109 -0
- package/src/conversation.mjs +18 -0
- package/src/core.mjs +863 -0
- package/src/cross-cloud-storage.mjs +100 -0
- package/src/delivery-capabilities.mjs +25 -0
- package/src/delivery.mjs +58 -0
- package/src/deployment-ownership.mjs +10 -0
- package/src/deployment-region.mjs +11 -0
- package/src/derive.mjs +31 -0
- package/src/design-build-flow.mjs +10 -0
- package/src/diagram-attention.mjs +24 -0
- package/src/discovery.mjs +359 -0
- package/src/document.mjs +170 -0
- package/src/emulation-feedback.mjs +61 -0
- package/src/emulation-guidance.mjs +37 -0
- package/src/engagement.mjs +274 -0
- package/src/engineering-client.mjs +73 -0
- package/src/engineering-defaults.mjs +17 -0
- package/src/engineering.css +31 -0
- package/src/enterprise-application.mjs +76 -0
- package/src/enterprise-aws-host-infrastructure.mjs +62 -0
- package/src/enterprise-aws-host-tests.mjs +15 -0
- package/src/enterprise-aws-infrastructure-tests.mjs +38 -0
- package/src/enterprise-aws-infrastructure.mjs +138 -0
- package/src/enterprise-google-infrastructure.mjs +84 -0
- package/src/enterprise-google-tests.mjs +25 -0
- package/src/estimate.mjs +190 -0
- package/src/existing-references.mjs +18 -0
- package/src/external-identities.mjs +17 -0
- package/src/faq-lambda-build.mjs +28 -0
- package/src/final-package.mjs +34 -0
- package/src/gcp-structured.mjs +104 -0
- package/src/generation-provider.mjs +7 -0
- package/src/generation-quality.mjs +69 -0
- package/src/handoff.mjs +141 -0
- package/src/ibm-registry.mjs +12 -0
- package/src/ibm-structured.mjs +103 -0
- package/src/identifiers.mjs +15 -0
- package/src/implementation-map.mjs +14 -0
- package/src/implementation-outcome.mjs +8 -0
- package/src/incomplete-terraform.mjs +9 -0
- package/src/integrations.ts +137 -0
- package/src/interface-contracts.mjs +30 -0
- package/src/invoice-application.mjs +66 -0
- package/src/invoice-aws-operations.mjs +136 -0
- package/src/invoice-aws.mjs +143 -0
- package/src/invoice-azure-operations.mjs +147 -0
- package/src/invoice-azure.mjs +134 -0
- package/src/invoice-builder.mjs +35 -0
- package/src/invoice-dependencies.mjs +43 -0
- package/src/invoice-gcp-operations.mjs +178 -0
- package/src/invoice-gcp.mjs +104 -0
- package/src/invoice-ibm-operations.mjs +278 -0
- package/src/invoice-ibm.mjs +142 -0
- package/src/invoice-operations.mjs +143 -0
- package/src/invoice-plan.mjs +45 -0
- package/src/invoice-region.mjs +6 -0
- package/src/invoice-spec-kit.mjs +61 -0
- package/src/invoice-state.mjs +7 -0
- package/src/invoice-terraform.mjs +29 -0
- package/src/invoice-tests.mjs +122 -0
- package/src/journey-workspace.css +106 -0
- package/src/journey.mjs +97 -0
- package/src/levels.mjs +145 -0
- package/src/local-agent.mjs +184 -0
- package/src/main.jsx +2481 -0
- package/src/model-policy-preflight.mjs +34 -0
- package/src/openai-voice.mjs +102 -0
- package/src/output-bundle.mjs +13 -0
- package/src/ownership-inspection.mjs +28 -0
- package/src/pack.mjs +201 -0
- package/src/pricing.mjs +85 -0
- package/src/production-state.mjs +8 -0
- package/src/project-copy.mjs +10 -0
- package/src/project-file.mjs +160 -0
- package/src/project-links.mjs +3 -0
- package/src/protected-state.mjs +43 -0
- package/src/public-faq-builder.mjs +19 -0
- package/src/public-faq-clouds.mjs +251 -0
- package/src/public-faq.mjs +122 -0
- package/src/pulumi.mjs +123 -0
- package/src/python-container.mjs +25 -0
- package/src/python-runtime.mjs +50 -0
- package/src/rag-handler.mjs +43 -0
- package/src/rag-web-assets.mjs +52 -0
- package/src/rag-web.mjs +68 -0
- package/src/records.mjs +177 -0
- package/src/resources.mjs +163 -0
- package/src/review.mjs +252 -0
- package/src/runtime/EnterpriseDocument.java +89 -0
- package/src/runtime/approved_http_destination.py +72 -0
- package/src/runtime/aws-lambda-qualification-lock.json +7 -0
- package/src/runtime/aws-python-lock.json +82 -0
- package/src/runtime/aws-web-python-lock.json +887 -0
- package/src/runtime/aws_json_model.py +42 -0
- package/src/runtime/azure-python-lock.json +863 -0
- package/src/runtime/azure-sources.json +8 -0
- package/src/runtime/azure-test-lock.json +23 -0
- package/src/runtime/azure_acceptance.py +192 -0
- package/src/runtime/azure_structured.py +153 -0
- package/src/runtime/bounded_rag.py +126 -0
- package/src/runtime/catalog_admin.py +70 -0
- package/src/runtime/cloud-sql-proxy-lock.json +8 -0
- package/src/runtime/cloud-sql-proxy-security-review.json +30 -0
- package/src/runtime/connector_cli.py +50 -0
- package/src/runtime/connector_sync.py +70 -0
- package/src/runtime/durable-python-lock.json +247 -0
- package/src/runtime/durable_workflow.py +325 -0
- package/src/runtime/enterprise-application-manifest.json +110 -0
- package/src/runtime/enterprise-application-sources.json +266 -0
- package/src/runtime/enterprise-auth-python-lock.json +592 -0
- package/src/runtime/enterprise-aws-host-bootstrap-source.json +1 -0
- package/src/runtime/enterprise-aws-host-probe-source.json +1 -0
- package/src/runtime/enterprise-aws-task-template.json +235 -0
- package/src/runtime/enterprise-document-lock.json +18 -0
- package/src/runtime/enterprise-document.Dockerfile +15 -0
- package/src/runtime/enterprise-runtime-aws-python-lock.json +1115 -0
- package/src/runtime/enterprise-runtime-azure-python-lock.json +1106 -0
- package/src/runtime/enterprise-runtime-gcp-python-lock.json +1060 -0
- package/src/runtime/enterprise-runtime-ibm-python-lock.json +1096 -0
- package/src/runtime/enterprise-runtime-python-lock.json +1058 -0
- package/src/runtime/enterprise-runtime.Dockerfile +15 -0
- package/src/runtime/enterprise-web-app.js +1 -0
- package/src/runtime/enterprise-web-entry.js +105 -0
- package/src/runtime/enterprise-web-lock.json +29 -0
- package/src/runtime/enterprise-web-notices.txt +229 -0
- package/src/runtime/enterprise-web.css +4 -0
- package/src/runtime/enterprise-web.html +8 -0
- package/src/runtime/enterprise_audit.py +91 -0
- package/src/runtime/enterprise_audit_schema.sql +25 -0
- package/src/runtime/enterprise_auth_schema.sql +67 -0
- package/src/runtime/enterprise_auth_store.py +241 -0
- package/src/runtime/enterprise_aws_host_bootstrap.py +120 -0
- package/src/runtime/enterprise_aws_host_probe.py +105 -0
- package/src/runtime/enterprise_aws_hosts.py +136 -0
- package/src/runtime/enterprise_aws_jobs.py +300 -0
- package/src/runtime/enterprise_aws_objects.py +93 -0
- package/src/runtime/enterprise_aws_transfer.py +83 -0
- package/src/runtime/enterprise_azure_batch_api.py +128 -0
- package/src/runtime/enterprise_azure_host.py +599 -0
- package/src/runtime/enterprise_azure_jobs.py +294 -0
- package/src/runtime/enterprise_azure_objects.py +234 -0
- package/src/runtime/enterprise_azure_pool.py +161 -0
- package/src/runtime/enterprise_azure_transfer.py +122 -0
- package/src/runtime/enterprise_configuration.py +136 -0
- package/src/runtime/enterprise_document.py +116 -0
- package/src/runtime/enterprise_document_cli.py +32 -0
- package/src/runtime/enterprise_document_contract.py +54 -0
- package/src/runtime/enterprise_document_job.py +109 -0
- package/src/runtime/enterprise_document_jobs.py +122 -0
- package/src/runtime/enterprise_document_jobs_schema.sql +23 -0
- package/src/runtime/enterprise_extraction_cache.py +186 -0
- package/src/runtime/enterprise_extraction_schema.sql +17 -0
- package/src/runtime/enterprise_google_jobs.py +275 -0
- package/src/runtime/enterprise_google_transfer.py +85 -0
- package/src/runtime/enterprise_http.py +212 -0
- package/src/runtime/enterprise_identity.py +52 -0
- package/src/runtime/enterprise_knowledge.py +152 -0
- package/src/runtime/enterprise_models.py +94 -0
- package/src/runtime/enterprise_native.py +181 -0
- package/src/runtime/enterprise_oauth.py +132 -0
- package/src/runtime/enterprise_operator.py +160 -0
- package/src/runtime/enterprise_parser_service.py +136 -0
- package/src/runtime/enterprise_parser_transport.py +68 -0
- package/src/runtime/enterprise_queries.py +255 -0
- package/src/runtime/enterprise_queries_schema.sql +27 -0
- package/src/runtime/enterprise_query_service.py +111 -0
- package/src/runtime/enterprise_secrets.py +110 -0
- package/src/runtime/enterprise_sessions.py +95 -0
- package/src/runtime/enterprise_sources.py +260 -0
- package/src/runtime/enterprise_web.py +39 -0
- package/src/runtime/enterprise_worker.py +68 -0
- package/src/runtime/export_contract_acceptance.py +18 -0
- package/src/runtime/faq-output-contract.json +40 -0
- package/src/runtime/faq-web-app.js +41 -0
- package/src/runtime/faq-web.css +1 -0
- package/src/runtime/faq-web.html +6 -0
- package/src/runtime/gcp-sources.json +9 -0
- package/src/runtime/gcp_acceptance.py +110 -0
- package/src/runtime/gcp_structured.py +150 -0
- package/src/runtime/http_acceptance.py +39 -0
- package/src/runtime/iap-web-app.js +36 -0
- package/src/runtime/iap-web-source.json +1 -0
- package/src/runtime/ibm-python-lock.json +856 -0
- package/src/runtime/ibm-sources.json +9 -0
- package/src/runtime/ibm_acceptance.py +133 -0
- package/src/runtime/ibm_structured.py +113 -0
- package/src/runtime/invoice-azapi-provider-lock.json +7 -0
- package/src/runtime/invoice-ibm-provider-lock.json +7 -0
- package/src/runtime/invoice-parser-lock.json +1199 -0
- package/src/runtime/invoice-parser-python-lock.json +56 -0
- package/src/runtime/invoice-parser.Dockerfile +190 -0
- package/src/runtime/invoice-sources.json +58 -0
- package/src/runtime/invoice-test-sources.json +24 -0
- package/src/runtime/invoice-web.css +2 -0
- package/src/runtime/invoice-web.html +12 -0
- package/src/runtime/invoice-web.js +112 -0
- package/src/runtime/invoice_configuration.py +17 -0
- package/src/runtime/invoice_document.py +95 -0
- package/src/runtime/invoice_extraction.py +96 -0
- package/src/runtime/invoice_http.py +129 -0
- package/src/runtime/invoice_identity.py +5 -0
- package/src/runtime/invoice_maintenance.py +40 -0
- package/src/runtime/invoice_native.py +77 -0
- package/src/runtime/invoice_object_store.py +153 -0
- package/src/runtime/invoice_operations.py +108 -0
- package/src/runtime/invoice_operator.py +79 -0
- package/src/runtime/invoice_operator_core.py +39 -0
- package/src/runtime/invoice_operator_job.py +147 -0
- package/src/runtime/invoice_parser_cli.py +17 -0
- package/src/runtime/invoice_parser_identity.py +39 -0
- package/src/runtime/invoice_parser_service.py +89 -0
- package/src/runtime/invoice_parser_transport.py +89 -0
- package/src/runtime/invoice_processing.py +24 -0
- package/src/runtime/invoice_retention.py +16 -0
- package/src/runtime/invoice_runtime_secrets.py +43 -0
- package/src/runtime/invoice_stage.py +80 -0
- package/src/runtime/invoice_web.py +39 -0
- package/src/runtime/invoice_worker.py +50 -0
- package/src/runtime/lambda_faq.py +5 -0
- package/src/runtime/parser-requirements.lock +4 -0
- package/src/runtime/policy_review.py +18 -0
- package/src/runtime/postgres-qualification-lock.json +21 -0
- package/src/runtime/public-faq-sources.json +22 -0
- package/src/runtime/public_catalog_admin.py +122 -0
- package/src/runtime/public_catalog_store.py +125 -0
- package/src/runtime/public_faq.py +144 -0
- package/src/runtime/public_faq_app.py +49 -0
- package/src/runtime/python-container-lock.json +18 -0
- package/src/runtime/rag-sources.json +10 -0
- package/src/runtime/rag_acceptance.py +60 -0
- package/src/runtime/rag_aws_adapter.py +95 -0
- package/src/runtime/rds-ca-source.json +9 -0
- package/src/runtime/rds-global-ca.pem +2888 -0
- package/src/runtime/state-contract-check-source.json +4 -0
- package/src/runtime/state_contract_check.py +338 -0
- package/src/runtime/structured-source.json +1 -0
- package/src/runtime/structured-web-app.js +71 -0
- package/src/runtime/structured-web-source.json +1 -0
- package/src/runtime/structured_http.py +105 -0
- package/src/runtime/structured_output.py +48 -0
- package/src/runtime/web-python-lock.json +802 -0
- package/src/runtime/web_user_admin.py +44 -0
- package/src/runtime/workflow_application.py +62 -0
- package/src/runtime/workflow_bindings.py +128 -0
- package/src/runtime/workflow_configuration.py +40 -0
- package/src/runtime/workflow_connection.py +105 -0
- package/src/runtime/workflow_contract.py +54 -0
- package/src/runtime/workflow_database.py +94 -0
- package/src/runtime/workflow_errors.py +6 -0
- package/src/runtime/workflow_http.py +178 -0
- package/src/runtime/workflow_identity.py +69 -0
- package/src/runtime/workflow_payload.py +118 -0
- package/src/runtime/workflow_process_stage.py +88 -0
- package/src/runtime/workflow_profile.py +91 -0
- package/src/runtime/workflow_review.py +106 -0
- package/src/runtime/workflow_role_bootstrap.py +104 -0
- package/src/runtime/workflow_schema.sql +72 -0
- package/src/runtime/workflow_worker.py +142 -0
- package/src/runtime-settings.mjs +15 -0
- package/src/s3-access.mjs +14 -0
- package/src/source-contracts.mjs +17 -0
- package/src/spec-kit.mjs +186 -0
- package/src/state-custody.mjs +32 -0
- package/src/state-inspection.mjs +45 -0
- package/src/state-spec-kit.mjs +13 -0
- package/src/storage-tests.mjs +25 -0
- package/src/structured-api.mjs +133 -0
- package/src/structured-contract.mjs +19 -0
- package/src/structured-operation-policy.mjs +2 -0
- package/src/structured-web.mjs +17 -0
- package/src/style.css +1 -0
- package/src/system-description.mjs +26 -0
- package/src/terraform-admission.mjs +66 -0
- package/src/terraform-composition.mjs +83 -0
- package/src/terraform-expressions.mjs +46 -0
- package/src/terraform-validation-coverage.mjs +11 -0
- package/src/terraform.mjs +128 -0
- package/src/text-statistics.mjs +51 -0
- package/src/upstream/openarchflow/format-payload.ts +26 -0
- package/src/upstream/openarchflow/import/mermaid.ts +205 -0
- package/src/upstream/openarchflow/import/sql-ddl.ts +109 -0
- package/src/upstream/openarchflow/layout.ts +54 -0
- package/src/upstream/openarchflow/providers/aws.ts +196 -0
- package/src/upstream/openarchflow/providers/azure.ts +126 -0
- package/src/upstream/openarchflow/providers/cloudnative.ts +71 -0
- package/src/upstream/openarchflow/providers/generic.ts +227 -0
- package/src/upstream/openarchflow/providers/integration.ts +22 -0
- package/src/upstream/openarchflow/providers/types.ts +2 -0
- package/src/upstream/openarchflow/templates.ts +937 -0
- package/src/upstream-catalog.mjs +10 -0
- package/src/useChatScroll.jsx +37 -0
- package/src/useEmulation.jsx +36 -0
- package/src/useEngineeringStatus.mjs +17 -0
- package/src/validation-receipt.mjs +10 -0
- package/src/visual-export.mjs +92 -0
- package/src/voice-conversation.mjs +117 -0
- package/src/work-intake.mjs +24 -0
- package/src/workflow-runtime-inventory.mjs +3 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
"""Strict contracts for the local agent boundary; no executable config accepted."""
|
|
2
|
+
from typing import Literal, Annotated
|
|
3
|
+
import re
|
|
4
|
+
from pydantic import BaseModel, ConfigDict, Field, PrivateAttr, StrictBool, StrictFloat, StrictInt, StrictStr, field_validator, model_validator
|
|
5
|
+
|
|
6
|
+
class Strict(BaseModel):
|
|
7
|
+
model_config=ConfigDict(extra='forbid')
|
|
8
|
+
|
|
9
|
+
class Position(Strict):
|
|
10
|
+
x:float=Field(ge=-100000,le=100000,allow_inf_nan=False)
|
|
11
|
+
y:float=Field(ge=-100000,le=100000,allow_inf_nan=False)
|
|
12
|
+
|
|
13
|
+
class OutputField(Strict):
|
|
14
|
+
name:StrictStr=Field(pattern=r'^[a-z][a-zA-Z0-9_]{0,39}$')
|
|
15
|
+
type:Literal['string','boolean','number']
|
|
16
|
+
description:StrictStr=Field(min_length=1,max_length=300)
|
|
17
|
+
values:list[StrictStr]|None=Field(default=None,min_length=1,max_length=30)
|
|
18
|
+
maxLength:StrictInt|None=Field(default=None,ge=1,le=4096)
|
|
19
|
+
@model_validator(mode='after')
|
|
20
|
+
def constraints(self):
|
|
21
|
+
if not self.description.strip():raise ValueError('Output field description is empty')
|
|
22
|
+
if self.values is not None and (self.type!='string' or len(set(self.values))!=len(self.values) or any(not v or len(v)>100 for v in self.values)):raise ValueError('Invalid string enumeration')
|
|
23
|
+
if self.maxLength is not None and self.type!='string':raise ValueError('Only strings have maxLength')
|
|
24
|
+
if self.values and any(len(v)>(self.maxLength or 4096) for v in self.values):raise ValueError('Enumeration values exceed the field length limit')
|
|
25
|
+
return self
|
|
26
|
+
|
|
27
|
+
class OutputContract(Strict):
|
|
28
|
+
instruction:StrictStr=Field(min_length=1,max_length=2000)
|
|
29
|
+
fields:list[OutputField]=Field(min_length=1,max_length=24)
|
|
30
|
+
@model_validator(mode='after')
|
|
31
|
+
def constraints(self):
|
|
32
|
+
if not self.instruction.strip():raise ValueError('Instruction is empty')
|
|
33
|
+
if len({f.name for f in self.fields})!=len(self.fields):raise ValueError('Duplicate output fields')
|
|
34
|
+
return self
|
|
35
|
+
|
|
36
|
+
class Configuration(Strict):
|
|
37
|
+
maxPdfBytes:StrictInt|None=Field(default=None,ge=1,le=20000000)
|
|
38
|
+
maxPdfPages:StrictInt|None=Field(default=None,ge=1,le=1000)
|
|
39
|
+
retentionDays:StrictInt|None=Field(default=None,ge=1,le=3653)
|
|
40
|
+
outputContract:OutputContract|None=None
|
|
41
|
+
region:str=Field(default='To be selected',max_length=100,description='For a proposed cloud deployment, use only its canonical region code, such as ap-southeast-2, australiaeast, australia-southeast1 or au-syd. Put city names, proposed status and unverified availability in description/assumptions, never appended to this machine field. Use To be selected when genuinely unresolved.')
|
|
42
|
+
classification:Literal['Public','Internal','Confidential']='Internal'
|
|
43
|
+
timeout:int=Field(default=5000,ge=100,le=60000)
|
|
44
|
+
containerMemoryBytes:StrictInt|None=Field(default=None,ge=1,le=9007199254740991)
|
|
45
|
+
memoryMb:StrictInt|None=Field(default=None,ge=128,le=10240)
|
|
46
|
+
functionTimeoutSeconds:StrictInt|None=Field(default=None,ge=1,le=900)
|
|
47
|
+
concurrency:StrictInt|None=Field(default=None,ge=1,le=1000)
|
|
48
|
+
maxInputBytes:StrictInt|None=Field(default=None,ge=1,le=1000000)
|
|
49
|
+
maxOutputTokens:StrictInt|None=Field(default=None,ge=1,le=4096)
|
|
50
|
+
modelId:StrictStr|None=Field(default=None,max_length=150,pattern=r'^[a-z0-9][a-z0-9.-]*:[0-9]+$')
|
|
51
|
+
logRetentionDays:Literal[1,3,5,7,14,30,60,90,120,150,180,365,400,545,731,1096,1827,2192,2557,2922,3288,3653]|None=None
|
|
52
|
+
|
|
53
|
+
class Measure(Strict):
|
|
54
|
+
label:str=Field(min_length=1,max_length=150)
|
|
55
|
+
path:str=Field(pattern=r'^[A-Za-z_][A-Za-z0-9_.]{0,79}$')
|
|
56
|
+
op:Literal['>=','<=','==']
|
|
57
|
+
target:StrictBool|StrictInt|StrictFloat|StrictStr
|
|
58
|
+
@field_validator('target')
|
|
59
|
+
@classmethod
|
|
60
|
+
def short(cls,value):
|
|
61
|
+
if isinstance(value,str) and len(value)>100:raise ValueError('Target too long')
|
|
62
|
+
return value
|
|
63
|
+
|
|
64
|
+
class Data(Strict):
|
|
65
|
+
label:str=Field(max_length=16000)
|
|
66
|
+
kind:Literal['source','predict','agent','guard','output','tool','resource','stream','history','features','train','registry','monitor','note']
|
|
67
|
+
provider:Literal['aws','azure','gcp','ibm','tools']
|
|
68
|
+
description:str=Field(default='',max_length=16000)
|
|
69
|
+
config:Configuration=Field(default_factory=Configuration)
|
|
70
|
+
inputPosition:Literal['left','right','top','bottom']='left'
|
|
71
|
+
outputPosition:Literal['left','right','top','bottom']='right'
|
|
72
|
+
origin:str=Field(default='proposed',max_length=100)
|
|
73
|
+
deploymentMode:Literal['reference','create']|None=None
|
|
74
|
+
poc:bool=False
|
|
75
|
+
role:Literal['data','ingest','index','llm','guard','human','app','output','model','monitor']|None=None
|
|
76
|
+
measures:list[Measure]|None=Field(default=None,max_length=10)
|
|
77
|
+
topologyGroup:str|None=Field(default=None,max_length=100)
|
|
78
|
+
service:str|None=Field(default=None,max_length=150)
|
|
79
|
+
category:str|None=Field(default=None,max_length=150)
|
|
80
|
+
binding:Literal['public-catalog-editor','cloud-storage-principal','aws-iam-principal','aws-bedrock-model','normalized-source-contract','web-browser','existing-service']|None=None
|
|
81
|
+
implementation:Literal['approval-workflow','invoice-workflow','public-faq-app','ibm-structured-app','gcp-structured-app','azure-structured-app','container-service','private-object-storage','rag-web-app','rag-user-pool','bounded-keyword-rag','iam-rag-api','bounded-rag-catalog','text-statistics','bedrock-summary','bedrock-structured','structured-api','structured-web-app','structured-user-pool','async-word-count','sqs-standard','sqs-dead-letter','word-count-results']|None=None
|
|
82
|
+
|
|
83
|
+
approvalProfile:dict|None=None
|
|
84
|
+
|
|
85
|
+
@field_validator('approvalProfile')
|
|
86
|
+
@classmethod
|
|
87
|
+
def approval_declaration(cls,value):
|
|
88
|
+
if value is None:return value
|
|
89
|
+
from src.runtime.workflow_bindings import application_declaration
|
|
90
|
+
return application_declaration(value).data
|
|
91
|
+
|
|
92
|
+
@model_validator(mode='after')
|
|
93
|
+
def named_component(self):
|
|
94
|
+
if self.approvalProfile is not None and (self.kind=='note' or self.binding or self.implementation!='approval-workflow' or self.provider!=self.approvalProfile['provider'] or self.origin!='proposed'):
|
|
95
|
+
raise ValueError('Approval profile requires its exact proposed provider/component contract; preserve existing ownership')
|
|
96
|
+
if self.config.containerMemoryBytes is not None and self.implementation!='container-service':raise ValueError('Exact container memory requires an explicit container-service contract')
|
|
97
|
+
if self.deploymentMode=='reference' and self.origin not in {'existing','imported Pulumi state'}:raise ValueError('Reference handling must preserve an existing system')
|
|
98
|
+
if self.binding and (self.origin not in {'existing','imported Pulumi state'} or self.deploymentMode=='create'):raise ValueError('Reference bindings identify existing systems and cannot provision a replacement')
|
|
99
|
+
if self.origin=='imported Pulumi state' and self.deploymentMode=='create':
|
|
100
|
+
raise ValueError('Imported cloud-state metadata identifies an existing deployment, not application source. Preserve its reference ownership; create a separate proposed deployment for new resources.')
|
|
101
|
+
if self.origin=='existing' and self.deploymentMode=='create' and (self.implementation!='container-service' or self.binding):
|
|
102
|
+
raise ValueError('Existing application source needs an explicit container-service contract without a reference binding.')
|
|
103
|
+
if self.kind != 'note' and len(self.label)>150:
|
|
104
|
+
raise ValueError('Architecture component names must have at most 150 characters.')
|
|
105
|
+
if self.kind != 'note' and not self.label.strip():
|
|
106
|
+
raise ValueError('Architecture components need a name; sticky notes may be empty.')
|
|
107
|
+
return self
|
|
108
|
+
|
|
109
|
+
class Node(Strict):
|
|
110
|
+
id:str=Field(min_length=1,max_length=100)
|
|
111
|
+
type:Literal['resource']='resource'
|
|
112
|
+
position:Position
|
|
113
|
+
data:Data
|
|
114
|
+
|
|
115
|
+
@model_validator(mode='after')
|
|
116
|
+
def approval_owner(self):
|
|
117
|
+
if self.data.approvalProfile is not None and self.data.approvalProfile['componentId']!=self.id:raise ValueError('Approval profile component identity differs from its owner')
|
|
118
|
+
return self
|
|
119
|
+
|
|
120
|
+
class Edge(Strict):
|
|
121
|
+
id:str=Field(min_length=1,max_length=150)
|
|
122
|
+
source:str=Field(max_length=100)
|
|
123
|
+
target:str=Field(max_length=100)
|
|
124
|
+
type:Literal['smoothstep']='smoothstep'
|
|
125
|
+
label:str=Field(default='data',max_length=150)
|
|
126
|
+
integration:Literal['approved-accounting-api','public-catalog-edit','browser-https','web-rag-api','oauth-pkce','jwt-authorizer','source-catalog-sync','http-api-lambda','lambda-catalog-read','bedrock-converse','s3-object-created','sqs-lambda','lambda-s3-read','lambda-dynamodb-write','sqs-redrive']|None=None
|
|
127
|
+
access:Literal['storage-object-admin','s3-read','s3-read-write','lambda-invoke','api-invoke']|None=None
|
|
128
|
+
|
|
129
|
+
class Project(Strict):
|
|
130
|
+
version:Literal[1]=1
|
|
131
|
+
revision:int=Field(ge=1)
|
|
132
|
+
name:str=Field(min_length=1,max_length=200)
|
|
133
|
+
provider:Literal['aws','azure','gcp','ibm','tools']='tools'
|
|
134
|
+
domain:Literal['maintenance','churn']|None=None
|
|
135
|
+
requirements:str=Field(default='',max_length=64000)
|
|
136
|
+
nodes:list[Node]=Field(max_length=200)
|
|
137
|
+
edges:list[Edge]=Field(max_length=600)
|
|
138
|
+
@model_validator(mode='after')
|
|
139
|
+
def integrity(self):
|
|
140
|
+
ids=[n.id for n in self.nodes]
|
|
141
|
+
if len(ids)!=len(set(ids)):raise ValueError('Duplicate block IDs')
|
|
142
|
+
wires=[e.id for e in self.edges]
|
|
143
|
+
if len(wires)!=len(set(wires)):raise ValueError('Duplicate wire IDs')
|
|
144
|
+
if any(e.source not in ids or e.target not in ids or e.source==e.target for e in self.edges):raise ValueError('Invalid wire endpoint')
|
|
145
|
+
from src.runtime.workflow_bindings import application_declaration
|
|
146
|
+
nodes={n.id:n for n in self.nodes}
|
|
147
|
+
for node in self.nodes:
|
|
148
|
+
if node.data.approvalProfile is None:continue
|
|
149
|
+
declaration=application_declaration(node.data.approvalProfile)
|
|
150
|
+
for ref in getattr(declaration,'references',[]):
|
|
151
|
+
source=nodes.get(ref['componentId'])
|
|
152
|
+
if source is None or source.data.kind=='note' or source.data.provider!=ref['provider']:raise ValueError('Native output reference must retain its graph component/provider')
|
|
153
|
+
return self
|
|
154
|
+
|
|
155
|
+
def validate_proposed_regions(project:Project):
|
|
156
|
+
"""Repair producer output without rejecting historical incoming designs."""
|
|
157
|
+
patterns={'aws':r'([a-z]{2}-[a-z]+|us-gov-[a-z]+)-[0-9]{1,2}','azure':r'[a-z][a-z0-9]{1,39}','gcp':r'[a-z]+-[a-z]+[0-9]{1,2}','ibm':r'[a-z]{2}-[a-z]+'}
|
|
158
|
+
for node in project.nodes:
|
|
159
|
+
data=node.data
|
|
160
|
+
if data.implementation!='invoice-workflow'or data.origin in {'existing','imported Pulumi state'}or data.config.region=='To be selected':continue
|
|
161
|
+
if data.provider not in patterns or not re.fullmatch(patterns[data.provider],data.config.region):raise ValueError('Invoice config.region must be the canonical provider region code only (for example au-syd for IBM). Preserve proposed placement and unverified availability in description/assumptions; do not ask the user to repair this machine field.')
|
|
162
|
+
|
|
163
|
+
class Proposal(Strict):
|
|
164
|
+
baseRevision:int=Field(ge=1)
|
|
165
|
+
title:str=Field(max_length=200)
|
|
166
|
+
body:str=Field(max_length=4000)
|
|
167
|
+
next:Project
|
|
168
|
+
|
|
169
|
+
class ToolRequest(Strict):
|
|
170
|
+
name:Literal['memory.lookup','knowledge.lookup','project.read','skills.lookup','pricing.lookup','pricing.aws_s3']
|
|
171
|
+
query:str=Field(default='',max_length=500)
|
|
172
|
+
|
|
173
|
+
class Scores(Strict):
|
|
174
|
+
cost:int=Field(ge=1,le=5)
|
|
175
|
+
risk:int=Field(ge=1,le=5)
|
|
176
|
+
scalability:int=Field(ge=1,le=5)
|
|
177
|
+
security:int=Field(ge=1,le=5)
|
|
178
|
+
|
|
179
|
+
class OptionAssessment(Strict):
|
|
180
|
+
provider:Literal['current','aws','azure','gcp','ibm']
|
|
181
|
+
scores:Scores
|
|
182
|
+
rationale:str=Field(min_length=1,max_length=1000)
|
|
183
|
+
|
|
184
|
+
class UsageUpdate(Strict):
|
|
185
|
+
key:Literal['requests_per_month','tokens_per_request','data_gb','endpoint_hours','events_per_month','training_hours']
|
|
186
|
+
value:float=Field(ge=0,le=1e12,allow_inf_nan=False)
|
|
187
|
+
sourceText:str=Field(min_length=1,max_length=500)
|
|
188
|
+
|
|
189
|
+
class RequirementUpdate(Strict):
|
|
190
|
+
rationale:str=Field(default='',max_length=1000)
|
|
191
|
+
id:str=Field(min_length=1,max_length=60)
|
|
192
|
+
statement:str=Field(min_length=1,max_length=1000)
|
|
193
|
+
sourceText:str=Field(min_length=1,max_length=500)
|
|
194
|
+
|
|
195
|
+
class RequirementTarget(Strict):
|
|
196
|
+
metric:str=Field(min_length=1,max_length=150)
|
|
197
|
+
op:Literal['>=','<=','==']
|
|
198
|
+
target:float=Field(allow_inf_nan=False)
|
|
199
|
+
unit:str=Field(default='',max_length=40)
|
|
200
|
+
|
|
201
|
+
class RequirementDraft(Strict):
|
|
202
|
+
rationale:str=Field(default='',max_length=1000)
|
|
203
|
+
basis:str=Field(default='',max_length=1000)
|
|
204
|
+
id:str=Field(pattern=r'^ai-[A-Za-z0-9-]{1,55}$')
|
|
205
|
+
statement:str=Field(min_length=1,max_length=1000)
|
|
206
|
+
kind:Literal['functional','nonfunctional']
|
|
207
|
+
category:Literal['security','reliability','availability','scalability','performance','maintainability','cost','compliance','other']
|
|
208
|
+
priority:Literal['must','should','could']='should'
|
|
209
|
+
links:list[str]=Field(default_factory=list,max_length=50)
|
|
210
|
+
measure:RequirementTarget|None=None
|
|
211
|
+
|
|
212
|
+
class DecisionUpdate(Strict):
|
|
213
|
+
subject:Literal['platform.cloud','cloud.region','data.country','infrastructure.tool']
|
|
214
|
+
value:str=Field(min_length=1,max_length=100)
|
|
215
|
+
sourceText:str=Field(min_length=1,max_length=500)
|
|
216
|
+
|
|
217
|
+
class ComponentReviewResult(Strict):
|
|
218
|
+
componentId:str=Field(min_length=1,max_length=100)
|
|
219
|
+
summary:str=Field(min_length=1,max_length=2000)
|
|
220
|
+
questions:list[str]=Field(default_factory=list,max_length=6)
|
|
221
|
+
@field_validator('questions')
|
|
222
|
+
@classmethod
|
|
223
|
+
def bounded_questions(cls,values):
|
|
224
|
+
if any(not q.strip() or len(q)>1000 for q in values):
|
|
225
|
+
raise ValueError('Block questions must contain 1–1000 characters.')
|
|
226
|
+
return values
|
|
227
|
+
|
|
228
|
+
class FollowUpQuestion(Strict):
|
|
229
|
+
question:str=Field(min_length=1,max_length=400)
|
|
230
|
+
why:str=Field(min_length=1,max_length=400)
|
|
231
|
+
example:str=Field(default='',max_length=400)
|
|
232
|
+
topic:Literal['application','users','data','systems','quality','cloud','budget']|None=None
|
|
233
|
+
|
|
234
|
+
class NodeFieldUpdate(Strict):
|
|
235
|
+
id:StrictStr=Field(min_length=1,max_length=100)
|
|
236
|
+
field:Literal['label','description','kind','provider','service','role','origin','binding','implementation','deploymentMode','topologyGroup','config.maxPdfBytes','config.maxPdfPages','config.retentionDays','config.region','config.classification','config.maxInputBytes','config.maxOutputTokens','config.memoryMb','config.containerMemoryBytes','config.functionTimeoutSeconds','config.concurrency','config.logRetentionDays','config.modelId','config.outputContract']
|
|
237
|
+
value:StrictStr|StrictInt|StrictBool|OutputContract|None
|
|
238
|
+
|
|
239
|
+
class EdgeFieldUpdate(Strict):
|
|
240
|
+
id:StrictStr=Field(min_length=1,max_length=150)
|
|
241
|
+
field:Literal['label','source','target','integration','access']
|
|
242
|
+
value:StrictStr|None
|
|
243
|
+
|
|
244
|
+
class DesignPatch(Strict):
|
|
245
|
+
projectName:StrictStr|None=Field(default=None,min_length=1,max_length=200)
|
|
246
|
+
baseRevision:int=Field(ge=1)
|
|
247
|
+
title:str=Field(min_length=1,max_length=200)
|
|
248
|
+
body:str=Field(default='',max_length=4000)
|
|
249
|
+
addNodes:list[Node]=Field(default_factory=list,max_length=200)
|
|
250
|
+
nodeUpdates:list[NodeFieldUpdate]=Field(default_factory=list,max_length=600)
|
|
251
|
+
removeNodeIds:list[Annotated[StrictStr,Field(min_length=1,max_length=100)]]=Field(default_factory=list,max_length=200)
|
|
252
|
+
addEdges:list[Edge]=Field(default_factory=list,max_length=600)
|
|
253
|
+
edgeUpdates:list[EdgeFieldUpdate]=Field(default_factory=list,max_length=1200)
|
|
254
|
+
removeEdgeIds:list[Annotated[StrictStr,Field(min_length=1,max_length=150)]]=Field(default_factory=list,max_length=600)
|
|
255
|
+
|
|
256
|
+
class ArchitectResponse(Strict):
|
|
257
|
+
_response_repair_attempts:int=PrivateAttr(default=0)
|
|
258
|
+
_model_response_encoding:str=PrivateAttr(default="full_response")
|
|
259
|
+
_model_response_json_bytes:int=PrivateAttr(default=0)
|
|
260
|
+
message:str=Field(min_length=1,max_length=16000)
|
|
261
|
+
patch:DesignPatch|None=None
|
|
262
|
+
componentReview:ComponentReviewResult|None=None
|
|
263
|
+
followUp:FollowUpQuestion|None=None
|
|
264
|
+
phase:Literal['Discovery','Architecture','Cost Analysis','Architecture Review']='Discovery'
|
|
265
|
+
proposal:Proposal|None=None
|
|
266
|
+
toolRequests:list[ToolRequest]|None=Field(default=None,max_length=3)
|
|
267
|
+
optionAssessment:list[OptionAssessment]|None=Field(default=None,max_length=5)
|
|
268
|
+
usageUpdates:list[UsageUpdate]|None=Field(default=None,max_length=6)
|
|
269
|
+
requirementDrafts:list[RequirementDraft]|None=Field(default=None,max_length=20)
|
|
270
|
+
requirementUpdates:list[RequirementUpdate]|None=Field(default=None,max_length=10)
|
|
271
|
+
decisionUpdates:list[DecisionUpdate]|None=Field(default=None,max_length=3)
|
|
272
|
+
|
|
273
|
+
class Message(Strict):
|
|
274
|
+
role:Literal['user','assistant']
|
|
275
|
+
text:str=Field(max_length=12000)
|
|
276
|
+
|
|
277
|
+
class ContextItem(Strict):
|
|
278
|
+
type:Literal['requirement','assumption','question','decision','discovery']
|
|
279
|
+
id:str=Field(min_length=1,max_length=60)
|
|
280
|
+
text:str=Field(max_length=1000)
|
|
281
|
+
|
|
282
|
+
class Attachment(Strict):
|
|
283
|
+
name:str=Field(min_length=1,max_length=180,pattern=r'^[^/\\\x00-\x1f]+$')
|
|
284
|
+
mime:Literal['image/png','image/jpeg','image/webp','application/pdf','application/vnd.openxmlformats-officedocument.wordprocessingml.document','text/plain','text/markdown','text/csv','application/json']
|
|
285
|
+
data:str=Field(min_length=1,max_length=5592408)
|
|
286
|
+
|
|
287
|
+
class ChatRequest(Strict):
|
|
288
|
+
activityId:str|None=Field(default=None,pattern=r'^[a-f0-9-]{36}$')
|
|
289
|
+
attachments:list[Attachment]=Field(default_factory=list,max_length=4)
|
|
290
|
+
origin:Literal['chat','canvas','workflow','document','preview','emulation','block-review']='chat'
|
|
291
|
+
projectId:str|None=Field(default=None,pattern=r'^[A-Za-z0-9-]{8,64}$')
|
|
292
|
+
message:str=Field(min_length=1,max_length=8000)
|
|
293
|
+
provider:Literal['claude-code','codex','copilot','gemini-cli','anthropic','openai','gemini-api','xai','bedrock']='claude-code'
|
|
294
|
+
model:str=Field(min_length=1,max_length=150)
|
|
295
|
+
effort:Literal['none','minimal','low','medium','high','xhigh','max','ultra']|None=None
|
|
296
|
+
project:Project
|
|
297
|
+
stage:Literal['discover','design','review','estimate','deploy','build']='discover'
|
|
298
|
+
context:list[ContextItem]=Field(default_factory=list,max_length=300)
|
|
299
|
+
history:list[Message]=Field(default_factory=list,max_length=16)
|
|
300
|
+
@field_validator('message','model')
|
|
301
|
+
@classmethod
|
|
302
|
+
def not_blank(cls,value):
|
|
303
|
+
if not value.strip():raise ValueError('Must not be blank')
|
|
304
|
+
return value
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def architect_output_schema(allow_tools=True):
|
|
308
|
+
"""Compact model wire protocol; host still returns the established proposal API."""
|
|
309
|
+
schema=ArchitectResponse.model_json_schema()
|
|
310
|
+
schema['properties']['proposal']={'type':'null','description':'Full snapshots are host-owned. Use patch for design edits.'}
|
|
311
|
+
if not allow_tools:schema['properties']['toolRequests']={'type':'null','description':'Evidence lookup budget is exhausted. Return the bounded final response using available evidence.'}
|
|
312
|
+
else:schema['$defs']['ToolRequest']['properties']['name']['enum']=['memory.lookup','knowledge.lookup','pricing.lookup']
|
|
313
|
+
schema.get('$defs',{}).pop('Proposal',None)
|
|
314
|
+
schema.get('$defs',{}).pop('Project',None)
|
|
315
|
+
return schema
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""One isolated Copilot SDK request using the official installed CLI and native sign-in."""
|
|
2
|
+
import asyncio, json, sys
|
|
3
|
+
from copilot import CopilotClient, RuntimeConnection
|
|
4
|
+
|
|
5
|
+
async def main():
|
|
6
|
+
config=json.load(sys.stdin)
|
|
7
|
+
async with CopilotClient(connection=RuntimeConnection.for_stdio(path=config['cli'],args=['--disable-builtin-mcps','--no-custom-instructions','--no-auto-update','--disable-client-telemetry']),working_directory=config['cwd'],use_logged_in_user=True,log_level='none') as client:
|
|
8
|
+
async with await client.create_session(model=None if config['model'] in ('auto','default') else config['model'],available_tools=[],tools=[],system_message={'mode':'replace','content':config['system']},skip_custom_instructions=True,enable_file_hooks=False,enable_skills=False,enable_config_discovery=False,enable_host_git_operations=False,enable_session_store=False,enable_session_telemetry=False,mcp_servers={},disabled_mcp_servers=['github-mcp-server','githubiq'],interactive_user=False) as session:
|
|
9
|
+
attachments=[{'type':'blob','data':i['data'],'mimeType':i['mime'],'displayName':i['name']} for i in config.get('images',[])]
|
|
10
|
+
result=await session.send_and_wait(config['prompt'],attachments=attachments or None,response_schema=config['schema'],timeout=130)
|
|
11
|
+
if not result:raise RuntimeError('No assistant response')
|
|
12
|
+
content=getattr(result.data,'content',None)
|
|
13
|
+
if not content:raise RuntimeError('No assistant content')
|
|
14
|
+
print(content)
|
|
15
|
+
|
|
16
|
+
if __name__=='__main__':asyncio.run(main())
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Explicit public dependency artifacts, hashed without importing their code.
|
|
2
|
+
|
|
3
|
+
No resolver, installer, URL fetch, account discovery or model-selected host path.
|
|
4
|
+
The operator registers the directory/read boundary; generated projects own their
|
|
5
|
+
dependency choices, portable builds and target qualification.
|
|
6
|
+
"""
|
|
7
|
+
import hashlib,json,os,re,stat
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from urllib.parse import urlsplit
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def _read(path,limit):
|
|
13
|
+
fd=os.open(path,os.O_RDONLY|os.O_NOFOLLOW|os.O_NONBLOCK)
|
|
14
|
+
try:
|
|
15
|
+
info=os.fstat(fd)
|
|
16
|
+
if not stat.S_ISREG(info.st_mode) or info.st_nlink!=1 or info.st_size>limit:raise ValueError('Dependency mirror requires bounded unlinked regular files')
|
|
17
|
+
with os.fdopen(fd,'rb',closefd=False)as stream:
|
|
18
|
+
value=stream.read(limit+1)
|
|
19
|
+
if len(value)>limit:raise ValueError('Dependency mirror file exceeds its bound')
|
|
20
|
+
return value
|
|
21
|
+
finally:os.close(fd)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def inspect_mirror(directory):
|
|
25
|
+
root=Path(directory)
|
|
26
|
+
if root.is_symlink() or not root.is_dir():raise ValueError('Use an operator-owned dependency mirror directory')
|
|
27
|
+
content=_read(root/'index.json',128000);index=json.loads(content)
|
|
28
|
+
if not isinstance(index,dict) or set(index)!={'format','version','artifacts'} or index['format']!='atmem.public-dependency-mirror' or type(index['version']) is not int or index['version']!=1 or not isinstance(index['artifacts'],list) or not 1<=len(index['artifacts'])<=64:raise ValueError('Unrecognized dependency catalog')
|
|
29
|
+
names=set();total=0
|
|
30
|
+
def text(value,limit=200):return isinstance(value,str) and bool(value) and len(value)<=limit and not any(ord(c)<32 for c in value)
|
|
31
|
+
for artifact in index['artifacts']:
|
|
32
|
+
if not isinstance(artifact,dict) or set(artifact)!={'file','sha256','bytes','name','version','kind','license','source','requiresPython','requiresDist'}:raise ValueError('Register complete bounded dependency metadata')
|
|
33
|
+
name=artifact['file']
|
|
34
|
+
if not isinstance(name,str) or not re.fullmatch(r'[A-Za-z0-9][A-Za-z0-9_.+-]{0,199}',name) or name=='index.json' or name in names:raise ValueError('Use unique flat dependency filenames')
|
|
35
|
+
names.add(name)
|
|
36
|
+
if not isinstance(artifact['sha256'],str) or not re.fullmatch(r'[a-f0-9]{64}',artifact['sha256']) or type(artifact['bytes']) is not int or not 0<artifact['bytes']<=32000000:raise ValueError('Dependency artifacts require bounded exact byte/digest pins')
|
|
37
|
+
for key in ['name','version','kind','license']:
|
|
38
|
+
if not text(artifact[key]):raise ValueError('Invalid dependency metadata')
|
|
39
|
+
if artifact['requiresPython'] is not None and not text(artifact['requiresPython']):raise ValueError('Invalid Python applicability')
|
|
40
|
+
if not isinstance(artifact['requiresDist'],list) or len(artifact['requiresDist'])>64 or any(not text(item,500) for item in artifact['requiresDist']):raise ValueError('Bound dependency requirements without truncation')
|
|
41
|
+
source=artifact['source']
|
|
42
|
+
if not isinstance(source,dict) or set(source)!={'url','metadataUrl','metadataSha256','retrievedAt'} or not isinstance(source['metadataSha256'],str) or not re.fullmatch(r'[a-f0-9]{64}',source['metadataSha256']):raise ValueError('Dependency source provenance is required')
|
|
43
|
+
from datetime import datetime
|
|
44
|
+
if not isinstance(source['retrievedAt'],str) or datetime.fromisoformat(source['retrievedAt']).tzinfo is None:raise ValueError('Source observation requires a timezone')
|
|
45
|
+
for key in ['url','metadataUrl']:
|
|
46
|
+
if not text(source[key],2000):raise ValueError('Invalid public dependency source URL')
|
|
47
|
+
url=urlsplit(source[key])
|
|
48
|
+
if url.scheme!='https' or not url.hostname or url.username or url.password or url.query or url.fragment:raise ValueError('Public source metadata cannot contain credentials, queries or fragments')
|
|
49
|
+
data=_read(root/name,32000000)
|
|
50
|
+
if len(data)!=artifact['bytes'] or hashlib.sha256(data).hexdigest()!=artifact['sha256']:raise ValueError('Dependency artifact differs from its registered bytes')
|
|
51
|
+
total+=len(data)
|
|
52
|
+
if total>64000000:raise ValueError('Dependency mirror exceeds its explicit total bound')
|
|
53
|
+
if set(os.listdir(root))!={'index.json',*names}:raise ValueError('Dependency mirror exposes undeclared entries')
|
|
54
|
+
return {'content':content,'sha256':hashlib.sha256(content).hexdigest(),'artifacts':len(names),'bytes':total}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Atomic, revision-bound model edits. No arbitrary JSON paths or requirements rewrite."""
|
|
2
|
+
from .contracts import Project,Proposal
|
|
3
|
+
|
|
4
|
+
def apply_design_patch(project,patch):
|
|
5
|
+
if patch.baseRevision!=project.revision:raise ValueError('Compact patch targets the wrong revision')
|
|
6
|
+
result=project.model_dump(exclude_none=True)
|
|
7
|
+
if patch.projectName is not None:result['name']=patch.projectName
|
|
8
|
+
nodes={n['id']:n for n in result['nodes']};edges={e['id']:e for e in result['edges']}
|
|
9
|
+
def remove(records,ids):
|
|
10
|
+
if len(ids)!=len(set(ids)):raise ValueError('Duplicate compact removal')
|
|
11
|
+
for id in ids:
|
|
12
|
+
if id not in records:raise ValueError('Compact removal targets an unknown ID: '+id)
|
|
13
|
+
del records[id]
|
|
14
|
+
remove(edges,patch.removeEdgeIds);remove(nodes,patch.removeNodeIds)
|
|
15
|
+
for node in patch.addNodes:
|
|
16
|
+
if node.id in nodes:raise ValueError('Compact add must not overwrite an existing component: '+node.id)
|
|
17
|
+
nodes[node.id]=node.model_dump(exclude_none=True)
|
|
18
|
+
for edge in patch.addEdges:
|
|
19
|
+
if edge.id in edges:raise ValueError('Compact add must not overwrite an existing connection: '+edge.id)
|
|
20
|
+
edges[edge.id]=edge.model_dump(exclude_none=True)
|
|
21
|
+
touched=set()
|
|
22
|
+
for update in patch.nodeUpdates:
|
|
23
|
+
if update.id not in nodes:raise ValueError('Compact update targets an unknown component: '+update.id)
|
|
24
|
+
key=('node',update.id,update.field)
|
|
25
|
+
if key in touched:raise ValueError('Duplicate compact field update')
|
|
26
|
+
touched.add(key);data=nodes[update.id]['data']
|
|
27
|
+
path=update.field.split('.')
|
|
28
|
+
if path[0]=='config':data=data.setdefault('config',{});field=path[1]
|
|
29
|
+
else:field=path[0]
|
|
30
|
+
value=update.value.model_dump(exclude_none=True) if hasattr(update.value,'model_dump') else update.value
|
|
31
|
+
if value is None:data.pop(field,None)
|
|
32
|
+
else:data[field]=value
|
|
33
|
+
for update in patch.edgeUpdates:
|
|
34
|
+
if update.id not in edges:raise ValueError('Compact update targets an unknown connection: '+update.id)
|
|
35
|
+
key=('edge',update.id,update.field)
|
|
36
|
+
if key in touched:raise ValueError('Duplicate compact field update')
|
|
37
|
+
touched.add(key)
|
|
38
|
+
if update.value is None:edges[update.id].pop(update.field,None)
|
|
39
|
+
else:edges[update.id][update.field]=update.value
|
|
40
|
+
result['nodes']=list(nodes.values());result['edges']=list(edges.values())
|
|
41
|
+
# Revalidate the whole candidate after all edits. Source project is never mutated.
|
|
42
|
+
candidate=Project.model_validate(result)
|
|
43
|
+
return Proposal(baseRevision=project.revision,title=patch.title,body=patch.body,next=candidate)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Version-scoped repairs for observed emulator defects, never application bypasses."""
|
|
2
|
+
from contextlib import contextmanager
|
|
3
|
+
|
|
4
|
+
PATCH={'id':'moto-s3-notification-version-id-v1','motoVersion':'5.2.5','reason':'Moto drops versionId from S3 notifications although its exact event key retains version_id.','reference':'https://docs.aws.amazon.com/AmazonS3/latest/userguide/notification-content-structure.html','scope':'Add only the exact triggering key version; do not look up current/latest objects or alter generated handlers.'}
|
|
5
|
+
|
|
6
|
+
@contextmanager
|
|
7
|
+
def s3_version_notifications():
|
|
8
|
+
import moto
|
|
9
|
+
from moto.s3 import notifications
|
|
10
|
+
if moto.__version__!=PATCH['motoVersion']:raise RuntimeError('Unqualified Moto version; revalidate adapter before upgrading')
|
|
11
|
+
original=notifications._get_s3_event
|
|
12
|
+
def event_with_version(event_name,bucket,key,notification_id):
|
|
13
|
+
event=original(event_name,bucket,key,notification_id)
|
|
14
|
+
version=getattr(key,'version_id',None)
|
|
15
|
+
if version and version!='null':
|
|
16
|
+
for record in event['Records']:record['s3']['object'].setdefault('versionId',version)
|
|
17
|
+
return event
|
|
18
|
+
notifications._get_s3_event=event_with_version
|
|
19
|
+
try:yield PATCH
|
|
20
|
+
finally:notifications._get_s3_event=original
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"""Provider-neutral emulation endpoint with a bounded local worker adapter.
|
|
2
|
+
|
|
3
|
+
A hosted adapter must enforce its own tenant identity and container isolation;
|
|
4
|
+
this local adapter is protected by the existing loopback session middleware.
|
|
5
|
+
"""
|
|
6
|
+
from datetime import datetime,timezone
|
|
7
|
+
import asyncio
|
|
8
|
+
import hashlib
|
|
9
|
+
import importlib.util
|
|
10
|
+
import json
|
|
11
|
+
import os
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
import shutil
|
|
14
|
+
import signal
|
|
15
|
+
import subprocess
|
|
16
|
+
import sys
|
|
17
|
+
import tempfile
|
|
18
|
+
from fastapi import APIRouter,HTTPException
|
|
19
|
+
from pydantic import BaseModel,ConfigDict
|
|
20
|
+
from typing import Literal
|
|
21
|
+
from .settings import setting
|
|
22
|
+
|
|
23
|
+
ROOT=Path(__file__).resolve().parents[1]
|
|
24
|
+
router=APIRouter()
|
|
25
|
+
LOCK=asyncio.Lock()
|
|
26
|
+
|
|
27
|
+
class EmulationRequest(BaseModel):
|
|
28
|
+
model_config=ConfigDict(extra='forbid')
|
|
29
|
+
project:dict
|
|
30
|
+
environment:Literal['staging','production']='staging'
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def emulate(payload):
|
|
34
|
+
if setting('EMULATION_ENABLED')=='0':
|
|
35
|
+
return {'version':1,'status':'unavailable','checks':[],'message':'Local emulation is disabled on this server.'}
|
|
36
|
+
declared_clouds={n.get('data',{}).get('provider') for n in payload.project.get('nodes',[]) if n.get('data',{}).get('implementation')=='private-object-storage'}
|
|
37
|
+
declared_faq=any(n.get('data',{}).get('implementation')=='public-faq-app' for n in payload.project.get('nodes',[]))
|
|
38
|
+
declared_structured=any((n.get('data',{}).get('provider'),n.get('data',{}).get('implementation')) in {('azure','azure-structured-app'),('gcp','gcp-structured-app'),('ibm','ibm-structured-app')} for n in payload.project.get('nodes',[]))
|
|
39
|
+
if not declared_faq and not declared_structured and not declared_clouds.intersection({'azure','gcp'}) and importlib.util.find_spec('moto') is None:
|
|
40
|
+
return {'version':1,'status':'unavailable','checks':[],'message':'The emulation worker is not installed on this server. Ask the server operator to enable the optional Moto backend.'}
|
|
41
|
+
node=shutil.which('node')
|
|
42
|
+
if not node:return {'version':1,'status':'unavailable','checks':[],'message':'The server-side generator is unavailable.'}
|
|
43
|
+
with tempfile.TemporaryDirectory(prefix='atmem-emulation-') as folder:
|
|
44
|
+
# No inherited SDK credentials, profiles, endpoint overrides, proxy or model keys.
|
|
45
|
+
env={'PATH':os.getenv('PATH',''),'HOME':folder,'TMPDIR':folder,'AWS_ACCESS_KEY_ID':'testing','AWS_SECRET_ACCESS_KEY':'testing','AWS_DEFAULT_REGION':'ap-southeast-2','AWS_EC2_METADATA_DISABLED':'true','AWS_CONFIG_FILE':str(Path(folder)/'no-config'),'AWS_SHARED_CREDENTIALS_FILE':str(Path(folder)/'no-credentials'),'PYTHONHASHSEED':'0'}
|
|
46
|
+
generated=subprocess.run([node,str(ROOT/'scripts/terraform-render.mjs')],input=json.dumps(payload.model_dump()),capture_output=True,text=True,cwd=ROOT,env=env,timeout=20)
|
|
47
|
+
if generated.returncode:raise ValueError('Invalid project for generation')
|
|
48
|
+
files=json.loads(generated.stdout)
|
|
49
|
+
digests={name:hashlib.sha256(content.encode()).hexdigest() for name,content in files.items()}
|
|
50
|
+
fingerprint=hashlib.sha256(json.dumps(digests,sort_keys=True,separators=(',',':')).encode()).hexdigest()
|
|
51
|
+
schedule=json.loads(files.get('resource-schedule.json','{}'))
|
|
52
|
+
cloud=schedule.get('provider')
|
|
53
|
+
faq=bool(schedule.get('publicFaqPlan',{}).get('entries'))
|
|
54
|
+
structured=bool(schedule.get('azureStructuredPlan',{}).get('entries') or schedule.get('gcpStructuredPlan',{}).get('entries') or schedule.get('ibmStructuredPlan',{}).get('entries'))
|
|
55
|
+
worker='public_faq_emulation_worker.py' if faq else 'structured_emulation_worker.py' if structured else 'storage_emulation_worker.py' if cloud in ('azure','gcp','ibm') else 'emulation_worker.py'
|
|
56
|
+
prerequisites={'azure':('azure.storage.blob',ROOT/'.local/cloud-emulators/node_modules/.bin/azurite-blob'),'gcp':('google.cloud.storage',ROOT/'.local/cloud-emulators/fake-gcs/fake-gcs-server')}
|
|
57
|
+
dependency='emulation_worker.py' if structured or faq else 'emulation_adapters.py'
|
|
58
|
+
worker_digest=hashlib.sha256((ROOT/'server'/worker).read_bytes()+(ROOT/'server'/dependency).read_bytes()).hexdigest()
|
|
59
|
+
metadata={'version':1,'generatedAt':datetime.now(timezone.utc).isoformat(),'workerDigest':worker_digest,'designRevision':payload.project.get('revision'),'environment':payload.environment,'artifactDigest':fingerprint,'fileDigests':digests,'cloudOperations':False,'terraformApply':'not_run'}
|
|
60
|
+
tf=json.loads(files['main.tf.json'])
|
|
61
|
+
if tf.get('locals',{}).get('unfinished_work'):
|
|
62
|
+
return {**metadata,'status':'blocked','checks':[],'message':'Resolve the generated implementation gaps before emulation.','blockers':tf['locals']['unfinished_work']}
|
|
63
|
+
if not faq and not structured and cloud in prerequisites:
|
|
64
|
+
module,binary=prerequisites[cloud]
|
|
65
|
+
try:installed=importlib.util.find_spec(module) is not None
|
|
66
|
+
except ModuleNotFoundError:installed=False
|
|
67
|
+
if not installed or not binary.is_file():return {**metadata,'status':'unavailable','checks':[],'message':f'The {cloud} storage emulator is not installed. Run the optional cloud emulator setup on the server.'}
|
|
68
|
+
for name,content in files.items():
|
|
69
|
+
relative=Path(name)
|
|
70
|
+
if relative.is_absolute() or '..' in relative.parts or '\\' in name or not isinstance(content,str):raise ValueError('Unsafe generated path')
|
|
71
|
+
target=Path(folder)/relative;target.parent.mkdir(parents=True,exist_ok=True);target.write_text(content)
|
|
72
|
+
# Kill the entire worker process group on timeout, including local API servers.
|
|
73
|
+
interpreter=sys.executable
|
|
74
|
+
if structured and schedule.get('ibmStructuredPlan',{}).get('entries'):
|
|
75
|
+
# IBM's current SDK/dateutil closure conflicts with the AWS SDK. Never
|
|
76
|
+
# mutate the server environment or pretend the omitted SDK test passed.
|
|
77
|
+
interpreter=setting('IBM_EMULATION_PYTHON') or str(ROOT/'.local/ibm-structured-runtime/bin/python')
|
|
78
|
+
if not Path(interpreter).is_file():return {**metadata,'status':'unavailable','checks':[],'message':'The isolated IBM Python 3.13 runtime is not installed. The server operator can run python3 scripts/setup-structured-runtime.py ibm.'}
|
|
79
|
+
process=subprocess.Popen([interpreter,'-I',str(ROOT/'server'/worker),folder],cwd=folder,env=env,stdout=subprocess.PIPE,stderr=subprocess.PIPE,text=True,start_new_session=True)
|
|
80
|
+
try:stdout,stderr=process.communicate(timeout=90)
|
|
81
|
+
except subprocess.TimeoutExpired:
|
|
82
|
+
os.killpg(process.pid,signal.SIGKILL);process.communicate();raise
|
|
83
|
+
process.stdout=stdout
|
|
84
|
+
|
|
85
|
+
if len(process.stdout)>1_000_000:raise ValueError('Emulation result exceeds bound')
|
|
86
|
+
try:result=json.loads(process.stdout)
|
|
87
|
+
except json.JSONDecodeError:return {**metadata,'status':'failed','checks':[],'message':'The isolated worker failed without a valid report; no success was recorded.'}
|
|
88
|
+
if process.returncode and result.get('status')=='passed':raise ValueError('Worker exit contradicts report')
|
|
89
|
+
return {**result,**metadata,'completedAt':datetime.now(timezone.utc).isoformat(),'workspaceCleanup':'passed'}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@router.post('/api/emulation/run')
|
|
93
|
+
async def emulate_route(payload:EmulationRequest):
|
|
94
|
+
if LOCK.locked():raise HTTPException(409,'An emulation is already running. Try again after it finishes.')
|
|
95
|
+
async with LOCK:
|
|
96
|
+
try:return await asyncio.to_thread(emulate,payload)
|
|
97
|
+
except subprocess.TimeoutExpired:raise HTTPException(504,'Emulation timed out; the worker was stopped and no pass was recorded.')
|
|
98
|
+
except (ValueError,OSError):raise HTTPException(422,'The generated project could not be emulated. Review the design and worker installation.')
|