@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,109 @@
|
|
|
1
|
+
"""One-file mounted job transport; never write source or result text to logs.
|
|
2
|
+
|
|
3
|
+
The launcher owns two distinct per-file mounts: read-only /input and writable
|
|
4
|
+
/output. This protocol is not a sandbox. Native workloads must separately remove
|
|
5
|
+
credentials, contain network/processes, bound resources and delete both mounts.
|
|
6
|
+
"""
|
|
7
|
+
import hashlib
|
|
8
|
+
import json
|
|
9
|
+
import os
|
|
10
|
+
import re
|
|
11
|
+
import stat
|
|
12
|
+
import uuid
|
|
13
|
+
from enterprise_document_contract import MAX_INPUT, MAX_TEXT, ExtractionError, document_result, media_type
|
|
14
|
+
|
|
15
|
+
MAX_ENVELOPE = MAX_TEXT * 6 + 4096
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def unique(pairs):
|
|
19
|
+
value = {}
|
|
20
|
+
for key, item in pairs:
|
|
21
|
+
if key in value: raise ExtractionError('invalid_document')
|
|
22
|
+
value[key] = item
|
|
23
|
+
return value
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def request(raw, declared, request_id):
|
|
27
|
+
if (not isinstance(raw, bytes) or not 0 < len(raw) <= MAX_INPUT or
|
|
28
|
+
not isinstance(request_id, str) or not re.fullmatch('[0-9a-f]{64}', request_id)):
|
|
29
|
+
raise ExtractionError('invalid_document')
|
|
30
|
+
return {'format': 'enterprise-document-job-v1', 'requestId': request_id,
|
|
31
|
+
'sha256': hashlib.sha256(raw).hexdigest(), 'mediaType': media_type(declared)}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def result(raw, declared, request_id, encoded):
|
|
35
|
+
"""Consumer check after the isolated job exits successfully, before caching."""
|
|
36
|
+
expected = request(raw, declared, request_id)
|
|
37
|
+
if not isinstance(encoded, bytes) or not 0 < len(encoded) <= MAX_ENVELOPE:
|
|
38
|
+
raise ExtractionError('invalid_document')
|
|
39
|
+
try:
|
|
40
|
+
value = json.loads(encoded, object_pairs_hook=unique)
|
|
41
|
+
if (not isinstance(value, dict) or set(value) != {'format', 'requestId', 'status', 'document'} or
|
|
42
|
+
value['format'] != expected['format'] or value['requestId'] != request_id or value['status'] != 'ok'):
|
|
43
|
+
raise ValueError()
|
|
44
|
+
return document_result(value['document'], raw, expected['mediaType'])
|
|
45
|
+
except Exception:
|
|
46
|
+
raise ExtractionError('invalid_document') from None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def read_file(directory_fd, name, limit):
|
|
50
|
+
fd = os.open(name, os.O_RDONLY | os.O_NOFOLLOW | os.O_NONBLOCK, dir_fd=directory_fd)
|
|
51
|
+
with os.fdopen(fd, 'rb') as stream:
|
|
52
|
+
info = os.fstat(stream.fileno())
|
|
53
|
+
if not stat.S_ISREG(info.st_mode) or not 0 < info.st_size <= limit:
|
|
54
|
+
raise ExtractionError('invalid_document')
|
|
55
|
+
raw = stream.read(limit + 1)
|
|
56
|
+
if not 0 < len(raw) <= limit or len(raw) != info.st_size:
|
|
57
|
+
raise ExtractionError('invalid_document')
|
|
58
|
+
return raw
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def write_result(directory_fd, value):
|
|
62
|
+
# Exclusive publication refuses an old/stale output and any symlink target.
|
|
63
|
+
# The launcher consumes only after successful process exit; a crashed partial
|
|
64
|
+
# temporary file cannot look like a complete result.
|
|
65
|
+
encoded = json.dumps(value, ensure_ascii=False, allow_nan=False, separators=(',', ':')).encode()
|
|
66
|
+
if len(encoded) > MAX_ENVELOPE: raise ExtractionError('extraction_failed')
|
|
67
|
+
temporary = '.result-' + uuid.uuid4().hex
|
|
68
|
+
try:
|
|
69
|
+
fd = os.open(temporary, os.O_WRONLY | os.O_CREAT | os.O_EXCL | os.O_NOFOLLOW, 0o600, dir_fd=directory_fd)
|
|
70
|
+
with os.fdopen(fd, 'wb') as stream:
|
|
71
|
+
stream.write(encoded)
|
|
72
|
+
stream.flush()
|
|
73
|
+
os.fsync(stream.fileno())
|
|
74
|
+
os.link(temporary, 'result.json', src_dir_fd=directory_fd, dst_dir_fd=directory_fd, follow_symlinks=False)
|
|
75
|
+
os.fsync(directory_fd)
|
|
76
|
+
finally:
|
|
77
|
+
try: os.unlink(temporary, dir_fd=directory_fd)
|
|
78
|
+
except FileNotFoundError: pass
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def execute(parser, input_path='/input', output_path='/output'):
|
|
82
|
+
"""Extract one document using fixed mount paths; errors contain no input data."""
|
|
83
|
+
source = target = None
|
|
84
|
+
try:
|
|
85
|
+
source = os.open(input_path, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW)
|
|
86
|
+
target = os.open(output_path, os.O_RDONLY | os.O_DIRECTORY | os.O_NOFOLLOW)
|
|
87
|
+
left, right = os.fstat(source), os.fstat(target)
|
|
88
|
+
if (left.st_dev, left.st_ino) == (right.st_dev, right.st_ino):
|
|
89
|
+
raise ExtractionError('invalid_document')
|
|
90
|
+
# Do not parse if the destination already exists (including a symlink).
|
|
91
|
+
try: os.stat('result.json', dir_fd=target, follow_symlinks=False)
|
|
92
|
+
except FileNotFoundError: pass
|
|
93
|
+
else: raise ExtractionError('invalid_document')
|
|
94
|
+
metadata = json.loads(read_file(source, 'request.json', 2048), object_pairs_hook=unique)
|
|
95
|
+
raw = read_file(source, 'document', MAX_INPUT)
|
|
96
|
+
if not isinstance(metadata, dict) or set(metadata) != {'format', 'requestId', 'sha256', 'mediaType'}:
|
|
97
|
+
raise ExtractionError('invalid_document')
|
|
98
|
+
expected = request(raw, metadata['mediaType'], metadata['requestId'])
|
|
99
|
+
if metadata != expected: raise ExtractionError('invalid_document')
|
|
100
|
+
document = document_result(parser.extract(raw, expected['mediaType']), raw, expected['mediaType'])
|
|
101
|
+
write_result(target, {'format': expected['format'], 'requestId': expected['requestId'], 'status': 'ok', 'document': document})
|
|
102
|
+
return {'status': 'ok'}, 0
|
|
103
|
+
except ExtractionError as failure:
|
|
104
|
+
return {'status': 'rejected', 'reason': failure.code}, 2
|
|
105
|
+
except Exception:
|
|
106
|
+
return {'status': 'rejected', 'reason': 'extraction_unavailable'}, 2
|
|
107
|
+
finally:
|
|
108
|
+
for fd in (target, source):
|
|
109
|
+
if fd is not None: os.close(fd)
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""Durable per-file cleanup intents, fencing and uncertain-creation settlement.
|
|
2
|
+
|
|
3
|
+
No source payload, title, identity, question, credential, endpoint or error body is
|
|
4
|
+
stored. Cleanup targets are derived by the owned adapter, never interpreted from
|
|
5
|
+
database text. All transactions end before native API work begins.
|
|
6
|
+
"""
|
|
7
|
+
from dataclasses import dataclass, field
|
|
8
|
+
import hashlib
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
import re
|
|
11
|
+
import uuid
|
|
12
|
+
from psycopg import sql
|
|
13
|
+
from enterprise_sources import SourceUnavailable
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@dataclass(frozen=True)
|
|
17
|
+
class FileJobLease:
|
|
18
|
+
request_id: str
|
|
19
|
+
token: str = field(repr=False)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class DocumentJobLedger:
|
|
23
|
+
def __init__(self, auth, account_id, target_sha256):
|
|
24
|
+
if not isinstance(account_id, str) or not re.fullmatch('[A-Za-z0-9_-]{1,128}', account_id): raise ValueError('Use the owned company account')
|
|
25
|
+
if not isinstance(target_sha256, str) or not re.fullmatch('[0-9a-f]{64}', target_sha256): raise ValueError('Use the owned immutable cleanup target')
|
|
26
|
+
self.auth, self.account_id, self.target = auth, account_id, target_sha256
|
|
27
|
+
with auth.transaction() as cursor:
|
|
28
|
+
expected=hashlib.sha256(Path(__file__).with_name('enterprise_document_jobs_schema.sql').read_bytes()).hexdigest()
|
|
29
|
+
if cursor.execute('SELECT version,source_sha256 FROM enterprise_document_jobs.schema_version').fetchone()!={'version':1,'source_sha256':expected}:
|
|
30
|
+
raise SourceUnavailable('document_job_schema_changed')
|
|
31
|
+
|
|
32
|
+
def parameters(self, identifier):
|
|
33
|
+
if not isinstance(identifier, str) or not re.fullmatch('[0-9a-f]{64}', identifier): raise ValueError('Use the owned per-file request')
|
|
34
|
+
return self.account_id, self.target, identifier
|
|
35
|
+
|
|
36
|
+
def allocate(self, identifier, *, ttl_seconds=240):
|
|
37
|
+
if type(ttl_seconds) is not int or not 180 <= ttl_seconds <= 600: raise ValueError('Use a bounded active execution deadline')
|
|
38
|
+
token = str(uuid.uuid4()); values = self.parameters(identifier)
|
|
39
|
+
with self.auth.transaction() as cursor:
|
|
40
|
+
self.auth.require_company(cursor, self.account_id)
|
|
41
|
+
row = cursor.execute("INSERT INTO enterprise_document_jobs.jobs(account_id,target_sha256,request_id,owner_token,phase,state,expires_at,next_attempt_at)VALUES(%s,%s,%s,%s,'input_pending','active',clock_timestamp()+make_interval(secs=>%s),clock_timestamp())ON CONFLICT DO NOTHING RETURNING request_id", (*values, token, ttl_seconds)).fetchone()
|
|
42
|
+
if not row: raise SourceUnavailable('document_execution_identity_conflict')
|
|
43
|
+
return FileJobLease(identifier, token)
|
|
44
|
+
|
|
45
|
+
def active(self, cursor, lease, *, lock=False):
|
|
46
|
+
row = cursor.execute("SELECT *,expires_at>clock_timestamp()AS current FROM enterprise_document_jobs.jobs WHERE account_id=%s AND target_sha256=%s AND request_id=%s" + (' FOR UPDATE' if lock else ''), self.parameters(lease.request_id)).fetchone()
|
|
47
|
+
if not row or row['state'] != 'active' or str(row['owner_token']) != lease.token or not row['current']:
|
|
48
|
+
raise SourceUnavailable('document_execution_lease_changed')
|
|
49
|
+
return row
|
|
50
|
+
|
|
51
|
+
def guard(self, lease):
|
|
52
|
+
with self.auth.transaction() as cursor: self.active(cursor, lease)
|
|
53
|
+
|
|
54
|
+
def advance(self, lease, phase):
|
|
55
|
+
previous = {'job_pending': 'input_pending', 'job_running': 'job_pending', 'succeeded': 'job_running'}
|
|
56
|
+
if phase not in previous: raise ValueError('Use the owned monotonic execution phases')
|
|
57
|
+
with self.auth.transaction() as cursor:
|
|
58
|
+
row = self.active(cursor, lease, lock=True)
|
|
59
|
+
if row['phase'] not in {previous[phase], phase}: raise SourceUnavailable('document_execution_phase_changed')
|
|
60
|
+
cursor.execute('UPDATE enterprise_document_jobs.jobs SET phase=%s,updated_at=clock_timestamp()WHERE account_id=%s AND target_sha256=%s AND request_id=%s', (phase, *self.parameters(lease.request_id)))
|
|
61
|
+
|
|
62
|
+
def mark_cleaning(self, cursor, identifier):
|
|
63
|
+
token = str(uuid.uuid4())
|
|
64
|
+
cursor.execute("UPDATE enterprise_document_jobs.jobs SET state='cleaning',cleanup_token=%s,cleanup_until=clock_timestamp()+interval '180 seconds',updated_at=clock_timestamp()WHERE account_id=%s AND target_sha256=%s AND request_id=%s", (token, *self.parameters(identifier)))
|
|
65
|
+
return FileJobLease(identifier, token)
|
|
66
|
+
|
|
67
|
+
def claim_owner(self, lease):
|
|
68
|
+
with self.auth.transaction() as cursor:
|
|
69
|
+
row = cursor.execute('SELECT *,cleanup_until>clock_timestamp()AS leased FROM enterprise_document_jobs.jobs WHERE account_id=%s AND target_sha256=%s AND request_id=%s FOR UPDATE', self.parameters(lease.request_id)).fetchone()
|
|
70
|
+
if not row or str(row['owner_token']) != lease.token or row['state'] != 'active': raise SourceUnavailable('document_execution_lease_changed')
|
|
71
|
+
return self.mark_cleaning(cursor, lease.request_id)
|
|
72
|
+
|
|
73
|
+
def claim_due(self):
|
|
74
|
+
with self.auth.transaction() as cursor:
|
|
75
|
+
row = cursor.execute("SELECT request_id FROM enterprise_document_jobs.jobs WHERE account_id=%s AND target_sha256=%s AND((state='active'AND expires_at<clock_timestamp())OR(state IN('pending','settling')AND next_attempt_at<=clock_timestamp())OR(state='cleaning'AND cleanup_until<clock_timestamp()))ORDER BY updated_at FOR UPDATE SKIP LOCKED LIMIT 1", (self.account_id, self.target)).fetchone()
|
|
76
|
+
return self.mark_cleaning(cursor, row['request_id']) if row else None
|
|
77
|
+
|
|
78
|
+
def cleanup_row(self, cursor, lease):
|
|
79
|
+
row = cursor.execute('SELECT *,cleanup_until>clock_timestamp()AS leased FROM enterprise_document_jobs.jobs WHERE account_id=%s AND target_sha256=%s AND request_id=%s FOR UPDATE', self.parameters(lease.request_id)).fetchone()
|
|
80
|
+
if not row or row['state'] != 'cleaning' or str(row['cleanup_token']) != lease.token or not row['leased']:
|
|
81
|
+
raise SourceUnavailable('document_cleanup_lease_changed')
|
|
82
|
+
return row
|
|
83
|
+
|
|
84
|
+
def completed(self, lease):
|
|
85
|
+
with self.auth.transaction() as cursor:
|
|
86
|
+
row = self.cleanup_row(cursor, lease)
|
|
87
|
+
final = row['phase'] == 'succeeded' or (row['settle_until'] is not None and cursor.execute('SELECT %s<=clock_timestamp()AS done', (row['settle_until'],)).fetchone()['done'])
|
|
88
|
+
if final:
|
|
89
|
+
cursor.execute('DELETE FROM enterprise_document_jobs.jobs WHERE account_id=%s AND target_sha256=%s AND request_id=%s', self.parameters(lease.request_id))
|
|
90
|
+
return True
|
|
91
|
+
# Retain incomplete/uncertain creations for delayed cleanup probes.
|
|
92
|
+
# These are scheduling bounds, not a provider deletion/retention SLA.
|
|
93
|
+
delay = [60, 300, 1800, 86400][min(row['settle_round'], 3)]
|
|
94
|
+
cursor.execute("UPDATE enterprise_document_jobs.jobs SET state='settling',cleanup_token=NULL,cleanup_until=NULL,settle_until=coalesce(settle_until,clock_timestamp()+interval '24 hours'),settle_round=least(settle_round+1,4),next_attempt_at=least(clock_timestamp()+make_interval(secs=>%s),coalesce(settle_until,clock_timestamp()+interval '24 hours')),updated_at=clock_timestamp()WHERE account_id=%s AND target_sha256=%s AND request_id=%s", (delay, *self.parameters(lease.request_id)))
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
def failed(self, lease):
|
|
98
|
+
with self.auth.transaction() as cursor:
|
|
99
|
+
row = self.cleanup_row(cursor, lease)
|
|
100
|
+
delay = min(600, 30 * 2 ** min(row['failures'], 5))
|
|
101
|
+
cursor.execute("UPDATE enterprise_document_jobs.jobs SET state='pending',cleanup_token=NULL,cleanup_until=NULL,failures=least(failures+1,1000000),next_attempt_at=clock_timestamp()+make_interval(secs=>%s),updated_at=clock_timestamp()WHERE account_id=%s AND target_sha256=%s AND request_id=%s", (delay, *self.parameters(lease.request_id)))
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def initialize_document_jobs(conn):
|
|
105
|
+
if conn.autocommit: raise ValueError('Use an owned operator transaction')
|
|
106
|
+
raw = Path(__file__).with_name('enterprise_document_jobs_schema.sql').read_bytes()
|
|
107
|
+
conn.execute(raw.decode()); conn.execute('INSERT INTO enterprise_document_jobs.schema_version VALUES(1,%s)', (hashlib.sha256(raw).hexdigest(),))
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def grant_document_jobs_runtime(conn, role):
|
|
111
|
+
if conn.autocommit or not isinstance(role, str) or not re.fullmatch('[A-Za-z0-9_.@-]{1,63}', role): raise ValueError('Use an owned worker identity and operator transaction')
|
|
112
|
+
expected = hashlib.sha256(Path(__file__).with_name('enterprise_document_jobs_schema.sql').read_bytes()).hexdigest()
|
|
113
|
+
if conn.execute('SELECT version,source_sha256 FROM enterprise_document_jobs.schema_version').fetchone() != (1, expected): raise ValueError('Review the exact fresh document-job schema')
|
|
114
|
+
flags = conn.execute('SELECT rolsuper,rolcreatedb,rolcreaterole,rolreplication,rolbypassrls,rolcanlogin FROM pg_roles WHERE rolname=%s', (role,)).fetchone()
|
|
115
|
+
if not flags or any(flags[:5]) or not flags[5] or conn.execute("SELECT has_schema_privilege(%s,'enterprise_document_jobs','CREATE')", (role,)).fetchone()[0]: raise ValueError('Use a nonadministrative worker identity')
|
|
116
|
+
for table in ['memberships', 'source_mappings', 'company_bindings', 'schema_version']:
|
|
117
|
+
if conn.execute('SELECT has_table_privilege(%s,%s,%s)', (role, 'enterprise_auth.' + table, 'INSERT,UPDATE,DELETE,TRUNCATE,TRIGGER,REFERENCES')).fetchone()[0]: raise ValueError('Document worker must not inherit company authority writes')
|
|
118
|
+
if conn.execute("SELECT has_table_privilege(%s,'enterprise_document_jobs.jobs','TRUNCATE,TRIGGER,REFERENCES')", (role,)).fetchone()[0]: raise ValueError('Document worker must not inherit administrative cleanup privileges')
|
|
119
|
+
target = sql.Identifier(role)
|
|
120
|
+
conn.execute(sql.SQL('GRANT USAGE ON SCHEMA enterprise_document_jobs TO {}').format(target))
|
|
121
|
+
conn.execute(sql.SQL('GRANT SELECT ON enterprise_document_jobs.schema_version TO {}').format(target))
|
|
122
|
+
conn.execute(sql.SQL('GRANT SELECT,INSERT,UPDATE,DELETE ON enterprise_document_jobs.jobs TO {}').format(target))
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
-- Fresh installation only; metadata-only cleanup intent, never document content.
|
|
2
|
+
CREATE SCHEMA enterprise_document_jobs;
|
|
3
|
+
REVOKE ALL ON SCHEMA enterprise_document_jobs FROM PUBLIC;
|
|
4
|
+
CREATE TABLE enterprise_document_jobs.schema_version(
|
|
5
|
+
version integer PRIMARY KEY CHECK(version=1),source_sha256 text NOT NULL
|
|
6
|
+
);
|
|
7
|
+
CREATE TABLE enterprise_document_jobs.jobs(
|
|
8
|
+
account_id text NOT NULL REFERENCES enterprise_auth.company_bindings(account_id),
|
|
9
|
+
target_sha256 text NOT NULL CHECK(target_sha256 ~ '^[0-9a-f]{64}$'),
|
|
10
|
+
request_id text NOT NULL CHECK(request_id ~ '^[0-9a-f]{64}$'),
|
|
11
|
+
owner_token uuid NOT NULL,
|
|
12
|
+
phase text NOT NULL CHECK(phase IN('input_pending','job_pending','job_running','succeeded')),
|
|
13
|
+
state text NOT NULL CHECK(state IN('active','cleaning','pending','settling')),
|
|
14
|
+
expires_at timestamptz NOT NULL,next_attempt_at timestamptz NOT NULL,
|
|
15
|
+
cleanup_token uuid,cleanup_until timestamptz,
|
|
16
|
+
failures integer NOT NULL DEFAULT 0 CHECK(failures BETWEEN 0 AND 1000000),
|
|
17
|
+
settle_until timestamptz,settle_round integer NOT NULL DEFAULT 0 CHECK(settle_round BETWEEN 0 AND 4),
|
|
18
|
+
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),updated_at timestamptz NOT NULL DEFAULT clock_timestamp(),
|
|
19
|
+
PRIMARY KEY(account_id,target_sha256,request_id),
|
|
20
|
+
CHECK((state='cleaning')=(cleanup_token IS NOT NULL AND cleanup_until IS NOT NULL))
|
|
21
|
+
);
|
|
22
|
+
CREATE INDEX enterprise_document_jobs_due ON enterprise_document_jobs.jobs(account_id,target_sha256,state,next_attempt_at,expires_at);
|
|
23
|
+
REVOKE ALL ON ALL TABLES IN SCHEMA enterprise_document_jobs FROM PUBLIC;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"""Encrypted immutable-byte extraction reuse, never cached source authorization.
|
|
2
|
+
|
|
3
|
+
Only a native source adapter can invoke ``from_source`` with its live version/access
|
|
4
|
+
check. Every hit still requires newly authorized source bytes. Native source and
|
|
5
|
+
company/job checks surround reads, extraction, publication and return. No source
|
|
6
|
+
URL, private title, user identity or credential is stored in the cache.
|
|
7
|
+
"""
|
|
8
|
+
from contextlib import contextmanager
|
|
9
|
+
import hashlib
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
import re
|
|
12
|
+
import time
|
|
13
|
+
import uuid
|
|
14
|
+
from psycopg import sql
|
|
15
|
+
from enterprise_document_contract import MAX_INPUT, MAX_TEXT, media_type
|
|
16
|
+
from enterprise_sources import SourceUnavailable
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ExtractionLeaseLost(SourceUnavailable):
|
|
20
|
+
pass
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def checked_text(value):
|
|
24
|
+
if not isinstance(value, str) or not value.strip() or '\x00' in value:
|
|
25
|
+
raise SourceUnavailable('document_extraction_unavailable')
|
|
26
|
+
try:
|
|
27
|
+
if len(value.encode('utf-8')) > MAX_TEXT:
|
|
28
|
+
raise ValueError()
|
|
29
|
+
except (ValueError, UnicodeError):
|
|
30
|
+
raise SourceUnavailable('document_extraction_unavailable') from None
|
|
31
|
+
return value
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ExtractionCache:
|
|
35
|
+
def __init__(self, auth, account_id, parser_revision, *, ttl_seconds=900, lease_seconds=150, wait_seconds=160):
|
|
36
|
+
if not isinstance(account_id, str) or not re.fullmatch('[A-Za-z0-9_-]{1,128}', account_id):
|
|
37
|
+
raise ValueError('Supply the owned company account')
|
|
38
|
+
if not isinstance(parser_revision, str) or not re.fullmatch('[0-9a-f]{64}', parser_revision):
|
|
39
|
+
raise ValueError('Supply the reviewed complete parser artifact revision')
|
|
40
|
+
if (type(ttl_seconds) is not int or not 60 <= ttl_seconds <= 3600
|
|
41
|
+
or type(lease_seconds) is not int or not 15 <= lease_seconds <= 300
|
|
42
|
+
or type(wait_seconds) is not int or not 1 <= wait_seconds <= 600):
|
|
43
|
+
raise ValueError('Use bounded owned cache deadlines')
|
|
44
|
+
self.auth, self.account_id, self.parser_revision = auth, account_id, parser_revision
|
|
45
|
+
self.ttl_seconds, self.lease_seconds, self.wait_seconds = ttl_seconds, lease_seconds, wait_seconds
|
|
46
|
+
|
|
47
|
+
@contextmanager
|
|
48
|
+
def transaction(self):
|
|
49
|
+
with self.auth.transaction() as cur:
|
|
50
|
+
cur.execute('SET LOCAL search_path=pg_catalog,enterprise_extractions,pg_temp')
|
|
51
|
+
yield cur
|
|
52
|
+
|
|
53
|
+
def identity(self, raw, declared):
|
|
54
|
+
if not isinstance(raw, bytes) or not 0 < len(raw) <= MAX_INPUT:
|
|
55
|
+
raise SourceUnavailable('source_document_unsupported')
|
|
56
|
+
try:
|
|
57
|
+
mime = media_type(declared)
|
|
58
|
+
except Exception:
|
|
59
|
+
raise SourceUnavailable('source_document_unsupported') from None
|
|
60
|
+
digest = hashlib.sha256(raw).hexdigest()
|
|
61
|
+
key = self.auth.box.fingerprint(['document-extraction-cache', self.account_id, self.parser_revision, mime], digest)
|
|
62
|
+
return key, digest, mime
|
|
63
|
+
|
|
64
|
+
def context(self, key):
|
|
65
|
+
return ['document-extraction-cache', self.account_id, self.parser_revision, key]
|
|
66
|
+
|
|
67
|
+
def claim(self, key):
|
|
68
|
+
"""Brief transaction only; parser/network work never holds SQL row locks."""
|
|
69
|
+
with self.transaction() as cur:
|
|
70
|
+
token = str(uuid.uuid4())
|
|
71
|
+
cur.execute("INSERT INTO enterprise_extractions.documents(account_id,parser_revision,content_key,state,lease_token,lease_until,expires_at)VALUES(%s,%s,%s,'building',%s,clock_timestamp()+make_interval(secs=>%s),clock_timestamp()+make_interval(secs=>%s))ON CONFLICT DO NOTHING",
|
|
72
|
+
(self.account_id, self.parser_revision, key, token, self.lease_seconds, self.lease_seconds))
|
|
73
|
+
row = cur.execute('SELECT *,expires_at>clock_timestamp()AS current,lease_until>clock_timestamp()AS leased FROM enterprise_extractions.documents WHERE account_id=%s AND parser_revision=%s AND content_key=%s FOR UPDATE',
|
|
74
|
+
(self.account_id, self.parser_revision, key)).fetchone()
|
|
75
|
+
if row['state'] == 'ready' and row['current']:
|
|
76
|
+
return 'ready', row['value_ciphertext']
|
|
77
|
+
if row['state'] == 'building' and row['leased']:
|
|
78
|
+
return ('claimed', token) if str(row['lease_token']) == token else ('waiting', None)
|
|
79
|
+
cur.execute("UPDATE enterprise_extractions.documents SET state='building',value_ciphertext=NULL,lease_token=%s,lease_until=clock_timestamp()+make_interval(secs=>%s),expires_at=clock_timestamp()+make_interval(secs=>%s)WHERE account_id=%s AND parser_revision=%s AND content_key=%s",
|
|
80
|
+
(token, self.lease_seconds, self.lease_seconds, self.account_id, self.parser_revision, key))
|
|
81
|
+
return 'claimed', token
|
|
82
|
+
|
|
83
|
+
def publish(self, key, token, value):
|
|
84
|
+
cipher = self.auth.box.seal(self.context(key), value)
|
|
85
|
+
with self.transaction() as cur:
|
|
86
|
+
row = cur.execute("UPDATE enterprise_extractions.documents SET state='ready',lease_token=NULL,lease_until=NULL,value_ciphertext=%s,expires_at=clock_timestamp()+make_interval(secs=>%s)WHERE account_id=%s AND parser_revision=%s AND content_key=%s AND state='building'AND lease_token=%s AND lease_until>clock_timestamp()RETURNING content_key",
|
|
87
|
+
(cipher, self.ttl_seconds, self.account_id, self.parser_revision, key, token)).fetchone()
|
|
88
|
+
if not row:
|
|
89
|
+
raise ExtractionLeaseLost('document_extraction_lease_changed')
|
|
90
|
+
|
|
91
|
+
def abandon(self, key, token):
|
|
92
|
+
with self.transaction() as cur:
|
|
93
|
+
cur.execute("DELETE FROM enterprise_extractions.documents WHERE account_id=%s AND parser_revision=%s AND content_key=%s AND state='building'AND lease_token=%s",
|
|
94
|
+
(self.account_id, self.parser_revision, key, token))
|
|
95
|
+
|
|
96
|
+
def open(self, key, digest, mime, cipher):
|
|
97
|
+
value = self.auth.box.open(self.context(key), cipher)
|
|
98
|
+
if set(value) != {'sha256', 'mediaType', 'text'} or value['sha256'] != digest or value['mediaType'] != mime:
|
|
99
|
+
raise SourceUnavailable('document_extraction_binding_invalid')
|
|
100
|
+
return checked_text(value['text'])
|
|
101
|
+
|
|
102
|
+
def wrap(self, extract, authorize):
|
|
103
|
+
if not callable(extract) or not callable(authorize):
|
|
104
|
+
raise ValueError('Supply owned extraction and live company/job authority')
|
|
105
|
+
return CachedDecoder(self, extract, authorize)
|
|
106
|
+
|
|
107
|
+
def purge(self, limit=1000):
|
|
108
|
+
if type(limit) is not int or not 1 <= limit <= 5000:
|
|
109
|
+
raise ValueError('Use a bounded expiration batch')
|
|
110
|
+
with self.transaction() as cur:
|
|
111
|
+
rows = cur.execute('DELETE FROM enterprise_extractions.documents WHERE(account_id,parser_revision,content_key)IN(SELECT account_id,parser_revision,content_key FROM enterprise_extractions.documents WHERE account_id=%s AND expires_at<clock_timestamp()ORDER BY expires_at FOR UPDATE SKIP LOCKED LIMIT %s)RETURNING content_key',
|
|
112
|
+
(self.account_id, limit)).fetchall()
|
|
113
|
+
return len(rows)
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
class CachedDecoder:
|
|
117
|
+
def __init__(self, cache, extract, authorize):
|
|
118
|
+
self.cache, self.extract, self.authorize = cache, extract, authorize
|
|
119
|
+
|
|
120
|
+
def __call__(self, raw, declared):
|
|
121
|
+
# A plain callback does not prove the surrounding native source has a live
|
|
122
|
+
# authorization/version check. Unreviewed adapters must not bypass it.
|
|
123
|
+
raise SourceUnavailable('source_validation_required')
|
|
124
|
+
|
|
125
|
+
def from_source(self, raw, declared, revalidate):
|
|
126
|
+
if not callable(revalidate):
|
|
127
|
+
raise ValueError('Supply the native live source check')
|
|
128
|
+
key, digest, mime = self.cache.identity(raw, declared)
|
|
129
|
+
deadline = time.monotonic() + self.cache.wait_seconds
|
|
130
|
+
def guard():
|
|
131
|
+
self.authorize()
|
|
132
|
+
revalidate()
|
|
133
|
+
guard()
|
|
134
|
+
while True:
|
|
135
|
+
self.authorize()
|
|
136
|
+
state, material = self.cache.claim(key)
|
|
137
|
+
if state == 'ready':
|
|
138
|
+
guard()
|
|
139
|
+
text = self.cache.open(key, digest, mime, material)
|
|
140
|
+
guard()
|
|
141
|
+
return text
|
|
142
|
+
if state == 'claimed':
|
|
143
|
+
try:
|
|
144
|
+
guard()
|
|
145
|
+
# A native per-file launcher must recheck authority while
|
|
146
|
+
# waiting and before releasing input/results, not only after
|
|
147
|
+
# a potentially slow cloud execution has already finished.
|
|
148
|
+
owned = getattr(self.extract, 'from_source', None)
|
|
149
|
+
text = checked_text(owned(raw, mime, guard) if callable(owned) else self.extract(raw, mime))
|
|
150
|
+
guard()
|
|
151
|
+
self.cache.publish(key, material, {'sha256': digest, 'mediaType': mime, 'text': text})
|
|
152
|
+
guard()
|
|
153
|
+
return text
|
|
154
|
+
except BaseException:
|
|
155
|
+
# A failed/denied/stale extractor never replaces a newer row.
|
|
156
|
+
self.cache.abandon(key, material)
|
|
157
|
+
raise
|
|
158
|
+
if time.monotonic() >= deadline:
|
|
159
|
+
raise SourceUnavailable('document_extraction_busy')
|
|
160
|
+
time.sleep(min(.2, max(0, deadline - time.monotonic())))
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
def initialize_extraction_cache(conn):
|
|
164
|
+
if conn.autocommit:
|
|
165
|
+
raise ValueError('Use an owned operator transaction')
|
|
166
|
+
source = Path(__file__).with_name('enterprise_extraction_schema.sql').read_bytes()
|
|
167
|
+
conn.execute(source.decode())
|
|
168
|
+
conn.execute('INSERT INTO enterprise_extractions.schema_version VALUES(1,%s)', (hashlib.sha256(source).hexdigest(),))
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
def grant_extraction_runtime(conn, role):
|
|
172
|
+
if conn.autocommit or not isinstance(role, str) or not re.fullmatch('[A-Za-z0-9_.@-]{1,63}', role):
|
|
173
|
+
raise ValueError('Use an owned nonadministrative runtime role and operator transaction')
|
|
174
|
+
expected = hashlib.sha256(Path(__file__).with_name('enterprise_extraction_schema.sql').read_bytes()).hexdigest()
|
|
175
|
+
if conn.execute('SELECT version,source_sha256 FROM enterprise_extractions.schema_version').fetchone() != (1, expected):
|
|
176
|
+
raise ValueError('Review the exact fresh cache schema before granting access')
|
|
177
|
+
flags = conn.execute('SELECT rolsuper,rolcreatedb,rolcreaterole,rolreplication,rolbypassrls,rolcanlogin FROM pg_roles WHERE rolname=%s', (role,)).fetchone()
|
|
178
|
+
if not flags or any(flags[:5]) or not flags[5] or conn.execute("SELECT has_schema_privilege(%s,'enterprise_extractions','CREATE')", (role,)).fetchone()[0]:
|
|
179
|
+
raise ValueError('Use a nonadministrative role that does not own the cache schema')
|
|
180
|
+
for table in ['memberships', 'source_mappings', 'company_bindings', 'schema_version']:
|
|
181
|
+
if conn.execute('SELECT has_table_privilege(%s,%s,%s)', (role, 'enterprise_auth.' + table, 'INSERT,UPDATE,DELETE,TRUNCATE,TRIGGER,REFERENCES')).fetchone()[0]:
|
|
182
|
+
raise ValueError('Cache runtime must not inherit authority-table write privileges')
|
|
183
|
+
target = sql.Identifier(role)
|
|
184
|
+
conn.execute(sql.SQL('GRANT USAGE ON SCHEMA enterprise_extractions TO {}').format(target))
|
|
185
|
+
conn.execute(sql.SQL('GRANT SELECT ON enterprise_extractions.schema_version TO {}').format(target))
|
|
186
|
+
conn.execute(sql.SQL('GRANT SELECT,INSERT,UPDATE,DELETE ON enterprise_extractions.documents TO {}').format(target))
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
-- Fresh owned installation only; cached derivatives are not source authority.
|
|
2
|
+
CREATE SCHEMA enterprise_extractions;
|
|
3
|
+
REVOKE ALL ON SCHEMA enterprise_extractions FROM PUBLIC;
|
|
4
|
+
CREATE TABLE enterprise_extractions.schema_version(
|
|
5
|
+
version integer PRIMARY KEY CHECK(version=1),source_sha256 text NOT NULL
|
|
6
|
+
);
|
|
7
|
+
CREATE TABLE enterprise_extractions.documents(
|
|
8
|
+
account_id text NOT NULL,parser_revision text NOT NULL CHECK(parser_revision ~ '^[0-9a-f]{64}$'),
|
|
9
|
+
content_key text NOT NULL CHECK(content_key ~ '^[0-9a-f]{64}$'),
|
|
10
|
+
state text NOT NULL CHECK(state IN('building','ready')),
|
|
11
|
+
lease_token uuid,lease_until timestamptz,value_ciphertext text,expires_at timestamptz NOT NULL,
|
|
12
|
+
PRIMARY KEY(account_id,parser_revision,content_key),
|
|
13
|
+
CHECK((state='ready')=(value_ciphertext IS NOT NULL)),
|
|
14
|
+
CHECK((state='building')=(lease_token IS NOT NULL AND lease_until IS NOT NULL))
|
|
15
|
+
);
|
|
16
|
+
CREATE INDEX enterprise_extractions_expiry ON enterprise_extractions.documents(account_id,expires_at);
|
|
17
|
+
REVOKE ALL ON ALL TABLES IN SCHEMA enterprise_extractions FROM PUBLIC;
|