devflow-engine 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- devflow_engine/__init__.py +3 -0
- devflow_engine/agentic_prompts.py +100 -0
- devflow_engine/agentic_runtime.py +398 -0
- devflow_engine/api_key_flow_harness.py +539 -0
- devflow_engine/api_keys.py +357 -0
- devflow_engine/bootstrap/__init__.py +2 -0
- devflow_engine/bootstrap/provision_from_template.py +84 -0
- devflow_engine/cli/__init__.py +0 -0
- devflow_engine/cli/app.py +7270 -0
- devflow_engine/core/__init__.py +0 -0
- devflow_engine/core/config.py +86 -0
- devflow_engine/core/logging.py +29 -0
- devflow_engine/core/paths.py +45 -0
- devflow_engine/core/toml_kv.py +33 -0
- devflow_engine/devflow_event_worker.py +1292 -0
- devflow_engine/devflow_state.py +201 -0
- devflow_engine/devin2/__init__.py +9 -0
- devflow_engine/devin2/agent_definition.py +120 -0
- devflow_engine/devin2/pi_runner.py +204 -0
- devflow_engine/devin_orchestration.py +69 -0
- devflow_engine/docs/prompts/anti-patterns.md +42 -0
- devflow_engine/docs/prompts/devin-agent-prompt.md +55 -0
- devflow_engine/docs/prompts/devin2-agent-prompt.md +81 -0
- devflow_engine/docs/prompts/examples/devin-vapi-clone-reference-exchange.json +85 -0
- devflow_engine/doctor/__init__.py +2 -0
- devflow_engine/doctor/triage.py +140 -0
- devflow_engine/error/__init__.py +0 -0
- devflow_engine/error/remediation.py +21 -0
- devflow_engine/errors/error_solver_dag.py +522 -0
- devflow_engine/errors/runtime_observability.py +67 -0
- devflow_engine/idea/__init__.py +4 -0
- devflow_engine/idea/actors.py +481 -0
- devflow_engine/idea/agentic.py +465 -0
- devflow_engine/idea/analyze.py +93 -0
- devflow_engine/idea/devin_chat_dag.py +1 -0
- devflow_engine/idea/diff.py +99 -0
- devflow_engine/idea/drafts.py +446 -0
- devflow_engine/idea/idea_creation_dag.py +643 -0
- devflow_engine/idea/ideation_enrichment.py +355 -0
- devflow_engine/idea/ideation_enrichment_worker.py +19 -0
- devflow_engine/idea/paths.py +28 -0
- devflow_engine/idea/promote.py +53 -0
- devflow_engine/idea/redaction.py +27 -0
- devflow_engine/idea/repo_tools.py +1277 -0
- devflow_engine/idea/response_mode.py +30 -0
- devflow_engine/idea/story_pipeline.py +1585 -0
- devflow_engine/idea/sufficiency.py +376 -0
- devflow_engine/idea/traditional_stories.py +1257 -0
- devflow_engine/implementation/__init__.py +0 -0
- devflow_engine/implementation/alembic_preflight.py +700 -0
- devflow_engine/implementation/dag.py +8450 -0
- devflow_engine/implementation/green_gate.py +93 -0
- devflow_engine/implementation/prompts.py +108 -0
- devflow_engine/implementation/test_runtime.py +623 -0
- devflow_engine/integration/__init__.py +19 -0
- devflow_engine/integration/agentic.py +66 -0
- devflow_engine/integration/dag.py +3539 -0
- devflow_engine/integration/prompts.py +114 -0
- devflow_engine/integration/supabase_schema.sql +31 -0
- devflow_engine/integration/supabase_sync.py +177 -0
- devflow_engine/llm/__init__.py +1 -0
- devflow_engine/llm/cli_one_shot.py +84 -0
- devflow_engine/llm/cli_stream.py +371 -0
- devflow_engine/llm/execution_context.py +26 -0
- devflow_engine/llm/invoke.py +1322 -0
- devflow_engine/llm/provider_api.py +304 -0
- devflow_engine/llm/repo_knowledge.py +588 -0
- devflow_engine/llm_primitives.py +315 -0
- devflow_engine/orchestration.py +62 -0
- devflow_engine/planning/__init__.py +0 -0
- devflow_engine/planning/analyze_repo.py +92 -0
- devflow_engine/planning/render_drafts.py +133 -0
- devflow_engine/playground/__init__.py +0 -0
- devflow_engine/playground/hooks.py +26 -0
- devflow_engine/playwright_workflow/__init__.py +5 -0
- devflow_engine/playwright_workflow/dag.py +1317 -0
- devflow_engine/process/__init__.py +5 -0
- devflow_engine/process/dag.py +59 -0
- devflow_engine/project_registration/__init__.py +3 -0
- devflow_engine/project_registration/dag.py +1581 -0
- devflow_engine/project_registry.py +109 -0
- devflow_engine/prompts/devin/generic/prompt.md +6 -0
- devflow_engine/prompts/devin/ideation/prompt.md +263 -0
- devflow_engine/prompts/devin/ideation/scenarios.md +5 -0
- devflow_engine/prompts/devin/ideation_loop/prompt.md +6 -0
- devflow_engine/prompts/devin/insight/prompt.md +11 -0
- devflow_engine/prompts/devin/insight/scenarios.md +5 -0
- devflow_engine/prompts/devin/intake/prompt.md +15 -0
- devflow_engine/prompts/devin/iterate/prompt.md +12 -0
- devflow_engine/prompts/devin/shared/eval_doctrine.md +9 -0
- devflow_engine/prompts/devin/shared/principles.md +246 -0
- devflow_engine/prompts/devin_eval/assessment/prompt.md +18 -0
- devflow_engine/prompts/idea/api_ideation_agent/prompt.md +8 -0
- devflow_engine/prompts/idea/api_insight_agent/prompt.md +8 -0
- devflow_engine/prompts/idea/response_doctrine/prompt.md +18 -0
- devflow_engine/prompts/implementation/dependency_assessment/prompt.md +12 -0
- devflow_engine/prompts/implementation/green/green/prompt.md +11 -0
- devflow_engine/prompts/implementation/green/node_config/prompt.md +3 -0
- devflow_engine/prompts/implementation/green_review/outcome_review/prompt.md +5 -0
- devflow_engine/prompts/implementation/green_review/prior_run_review/prompt.md +5 -0
- devflow_engine/prompts/implementation/red/prompt.md +27 -0
- devflow_engine/prompts/implementation/redreview/prompt.md +23 -0
- devflow_engine/prompts/implementation/redreview_repair/prompt.md +16 -0
- devflow_engine/prompts/implementation/setupdoc/prompt.md +10 -0
- devflow_engine/prompts/implementation/story_planning/prompt.md +13 -0
- devflow_engine/prompts/implementation/test_design/prompt.md +27 -0
- devflow_engine/prompts/integration/README.md +185 -0
- devflow_engine/prompts/integration/green/example.md +67 -0
- devflow_engine/prompts/integration/green/green/prompt.md +10 -0
- devflow_engine/prompts/integration/green/node_config/prompt.md +42 -0
- devflow_engine/prompts/integration/green/past_prompts/20260417T212300/green/prompt.md +15 -0
- devflow_engine/prompts/integration/green/past_prompts/20260417T212300/node_config/prompt.md +42 -0
- devflow_engine/prompts/integration/green_enrich/example.md +79 -0
- devflow_engine/prompts/integration/green_enrich/green_enrich/prompt.md +9 -0
- devflow_engine/prompts/integration/green_enrich/node_config/prompt.md +41 -0
- devflow_engine/prompts/integration/green_enrich/past_prompts/20260417T212300/green_enrich/prompt.md +14 -0
- devflow_engine/prompts/integration/green_enrich/past_prompts/20260417T212300/node_config/prompt.md +41 -0
- devflow_engine/prompts/integration/red/code_repair/prompt.md +12 -0
- devflow_engine/prompts/integration/red/example.md +152 -0
- devflow_engine/prompts/integration/red/node_config/prompt.md +86 -0
- devflow_engine/prompts/integration/red/past_prompts/20260417T212300/code_repair/prompt.md +19 -0
- devflow_engine/prompts/integration/red/past_prompts/20260417T212300/node_config/prompt.md +84 -0
- devflow_engine/prompts/integration/red/past_prompts/20260417T212300/red/prompt.md +16 -0
- devflow_engine/prompts/integration/red/past_prompts/20260417T212300/red_repair/prompt.md +15 -0
- devflow_engine/prompts/integration/red/past_prompts/20260417T215032/code_repair/prompt.md +10 -0
- devflow_engine/prompts/integration/red/past_prompts/20260417T215032/node_config/prompt.md +84 -0
- devflow_engine/prompts/integration/red/past_prompts/20260417T215032/red_repair/prompt.md +11 -0
- devflow_engine/prompts/integration/red/red/prompt.md +11 -0
- devflow_engine/prompts/integration/red/red_repair/prompt.md +12 -0
- devflow_engine/prompts/integration/red_review/example.md +71 -0
- devflow_engine/prompts/integration/red_review/node_config/prompt.md +41 -0
- devflow_engine/prompts/integration/red_review/past_prompts/20260417T212300/node_config/prompt.md +41 -0
- devflow_engine/prompts/integration/red_review/past_prompts/20260417T212300/red_review/prompt.md +15 -0
- devflow_engine/prompts/integration/red_review/red_review/prompt.md +9 -0
- devflow_engine/prompts/integration/resolve/example.md +111 -0
- devflow_engine/prompts/integration/resolve/node_config/prompt.md +64 -0
- devflow_engine/prompts/integration/resolve/past_prompts/20260417T212300/node_config/prompt.md +64 -0
- devflow_engine/prompts/integration/resolve/past_prompts/20260417T212300/resolve_implicated_users/prompt.md +15 -0
- devflow_engine/prompts/integration/resolve/past_prompts/20260417T212300/resolve_side_effects/prompt.md +15 -0
- devflow_engine/prompts/integration/resolve/resolve_implicated_users/prompt.md +10 -0
- devflow_engine/prompts/integration/resolve/resolve_side_effects/prompt.md +10 -0
- devflow_engine/prompts/integration/validate/build_idea_acceptance_coverage/prompt.md +12 -0
- devflow_engine/prompts/integration/validate/code_repair/prompt.md +13 -0
- devflow_engine/prompts/integration/validate/example.md +143 -0
- devflow_engine/prompts/integration/validate/node_config/prompt.md +87 -0
- devflow_engine/prompts/integration/validate/past_prompts/20260417T212300/code_repair/prompt.md +19 -0
- devflow_engine/prompts/integration/validate/past_prompts/20260417T212300/node_config/prompt.md +67 -0
- devflow_engine/prompts/integration/validate/past_prompts/20260417T212300/validate_enrich_gate/prompt.md +17 -0
- devflow_engine/prompts/integration/validate/past_prompts/20260417T212300/validate_repair/prompt.md +16 -0
- devflow_engine/prompts/integration/validate/past_prompts/20260417T215032/code_repair/prompt.md +10 -0
- devflow_engine/prompts/integration/validate/past_prompts/20260417T215032/node_config/prompt.md +67 -0
- devflow_engine/prompts/integration/validate/past_prompts/20260417T215032/validate_repair/prompt.md +9 -0
- devflow_engine/prompts/integration/validate/validate_enrich_gate/prompt.md +10 -0
- devflow_engine/prompts/integration/validate/validate_repair/prompt.md +20 -0
- devflow_engine/prompts/integration/write_workflows/example.md +100 -0
- devflow_engine/prompts/integration/write_workflows/node_config/prompt.md +44 -0
- devflow_engine/prompts/integration/write_workflows/past_prompts/20260417T212300/node_config/prompt.md +44 -0
- devflow_engine/prompts/integration/write_workflows/past_prompts/20260417T212300/write_workflows/prompt.md +17 -0
- devflow_engine/prompts/integration/write_workflows/write_workflows/prompt.md +11 -0
- devflow_engine/prompts/iterate/README.md +7 -0
- devflow_engine/prompts/iterate/coder/prompt.md +11 -0
- devflow_engine/prompts/iterate/framer/prompt.md +11 -0
- devflow_engine/prompts/iterate/iterator/prompt.md +13 -0
- devflow_engine/prompts/iterate/observer/prompt.md +11 -0
- devflow_engine/prompts/recovery/diagnosis/prompt.md +7 -0
- devflow_engine/prompts/recovery/execution/prompt.md +8 -0
- devflow_engine/prompts/recovery/execution_verification/prompt.md +7 -0
- devflow_engine/prompts/recovery/failure_investigation/prompt.md +10 -0
- devflow_engine/prompts/recovery/preflight_health_repo_repair/prompt.md +8 -0
- devflow_engine/prompts/recovery/remediation_execution/prompt.md +11 -0
- devflow_engine/prompts/recovery/root_cause_investigation/prompt.md +12 -0
- devflow_engine/prompts/scope_idea/doctrine/prompt.md +7 -0
- devflow_engine/prompts/source_doc_eval/document/prompt.md +6 -0
- devflow_engine/prompts/source_doc_eval/targeted_mutation/prompt.md +9 -0
- devflow_engine/prompts/source_doc_mutation/domain_entities/prompt.md +6 -0
- devflow_engine/prompts/source_doc_mutation/product_brief/prompt.md +6 -0
- devflow_engine/prompts/source_doc_mutation/project_doc_coherence/prompt.md +7 -0
- devflow_engine/prompts/source_doc_mutation/project_doc_render/prompt.md +9 -0
- devflow_engine/prompts/source_doc_mutation/source_doc_coherence/prompt.md +5 -0
- devflow_engine/prompts/source_doc_mutation/source_doc_enrichment_coherence/prompt.md +6 -0
- devflow_engine/prompts/source_doc_mutation/user_workflows/prompt.md +6 -0
- devflow_engine/prompts/source_scope/doctrine/prompt.md +10 -0
- devflow_engine/prompts/ui_grounding/doctrine/prompt.md +7 -0
- devflow_engine/recovery/__init__.py +3 -0
- devflow_engine/recovery/dag.py +2609 -0
- devflow_engine/recovery/models.py +220 -0
- devflow_engine/refactor.py +93 -0
- devflow_engine/registry/__init__.py +1 -0
- devflow_engine/registry/cards.py +238 -0
- devflow_engine/registry/domain_normalize.py +60 -0
- devflow_engine/registry/effects.py +65 -0
- devflow_engine/registry/enforce_report.py +150 -0
- devflow_engine/registry/module_cards_classify.py +164 -0
- devflow_engine/registry/module_cards_draft.py +184 -0
- devflow_engine/registry/module_cards_gate.py +59 -0
- devflow_engine/registry/packages.py +347 -0
- devflow_engine/registry/pathways.py +323 -0
- devflow_engine/review/__init__.py +11 -0
- devflow_engine/review/dag.py +588 -0
- devflow_engine/review/review_story.py +67 -0
- devflow_engine/scope_idea/__init__.py +3 -0
- devflow_engine/scope_idea/agentic.py +39 -0
- devflow_engine/scope_idea/dag.py +1069 -0
- devflow_engine/scope_idea/models.py +175 -0
- devflow_engine/skills/builtins/devflow/queue_failure_investigation/SKILL.md +112 -0
- devflow_engine/skills/builtins/devflow/queue_idea_to_story/SKILL.md +120 -0
- devflow_engine/skills/builtins/devflow/queue_integration/SKILL.md +105 -0
- devflow_engine/skills/builtins/devflow/queue_recovery/SKILL.md +108 -0
- devflow_engine/skills/builtins/devflow/queue_runtime_core/SKILL.md +155 -0
- devflow_engine/skills/builtins/devflow/queue_story_implementation/SKILL.md +122 -0
- devflow_engine/skills/builtins/devin/idea_to_story_handoff/SKILL.md +120 -0
- devflow_engine/skills/builtins/devin/ideation/SKILL.md +168 -0
- devflow_engine/skills/builtins/devin/ideation/state-and-phrasing-reference.md +18 -0
- devflow_engine/skills/builtins/devin/insight/SKILL.md +22 -0
- devflow_engine/skills/registry.example.yaml +42 -0
- devflow_engine/source_doc_assumptions.py +291 -0
- devflow_engine/source_doc_mutation_dag.py +1606 -0
- devflow_engine/source_doc_mutation_eval.py +417 -0
- devflow_engine/source_doc_mutation_worker.py +25 -0
- devflow_engine/source_docs_schema.py +207 -0
- devflow_engine/source_docs_updater.py +309 -0
- devflow_engine/source_scope/__init__.py +15 -0
- devflow_engine/source_scope/agentic.py +45 -0
- devflow_engine/source_scope/dag.py +1626 -0
- devflow_engine/source_scope/models.py +177 -0
- devflow_engine/stores/__init__.py +0 -0
- devflow_engine/stores/execution_store.py +3534 -0
- devflow_engine/story/__init__.py +0 -0
- devflow_engine/story/contracts.py +160 -0
- devflow_engine/story/discovery.py +47 -0
- devflow_engine/story/evidence.py +118 -0
- devflow_engine/story/hashing.py +27 -0
- devflow_engine/story/implemented_queue_purge.py +148 -0
- devflow_engine/story/indexer.py +105 -0
- devflow_engine/story/io.py +20 -0
- devflow_engine/story/markdown_contracts.py +298 -0
- devflow_engine/story/reconciliation.py +408 -0
- devflow_engine/story/validate_stories.py +149 -0
- devflow_engine/story/validate_tests_story.py +512 -0
- devflow_engine/story/validation.py +133 -0
- devflow_engine/ui_grounding/__init__.py +11 -0
- devflow_engine/ui_grounding/agentic.py +31 -0
- devflow_engine/ui_grounding/dag.py +874 -0
- devflow_engine/ui_grounding/models.py +224 -0
- devflow_engine/ui_grounding/pencil_bridge.py +247 -0
- devflow_engine/vendor/__init__.py +0 -0
- devflow_engine/vendor/datalumina_genai/__init__.py +11 -0
- devflow_engine/vendor/datalumina_genai/core/__init__.py +0 -0
- devflow_engine/vendor/datalumina_genai/core/exceptions.py +9 -0
- devflow_engine/vendor/datalumina_genai/core/nodes/__init__.py +0 -0
- devflow_engine/vendor/datalumina_genai/core/nodes/agent.py +48 -0
- devflow_engine/vendor/datalumina_genai/core/nodes/agent_streaming_node.py +26 -0
- devflow_engine/vendor/datalumina_genai/core/nodes/base.py +89 -0
- devflow_engine/vendor/datalumina_genai/core/nodes/concurrent.py +30 -0
- devflow_engine/vendor/datalumina_genai/core/nodes/router.py +69 -0
- devflow_engine/vendor/datalumina_genai/core/schema.py +72 -0
- devflow_engine/vendor/datalumina_genai/core/task.py +52 -0
- devflow_engine/vendor/datalumina_genai/core/validate.py +139 -0
- devflow_engine/vendor/datalumina_genai/core/workflow.py +200 -0
- devflow_engine/worker.py +1086 -0
- devflow_engine/worker_guard.py +233 -0
- devflow_engine-1.0.0.dist-info/METADATA +235 -0
- devflow_engine-1.0.0.dist-info/RECORD +393 -0
- devflow_engine-1.0.0.dist-info/WHEEL +4 -0
- devflow_engine-1.0.0.dist-info/entry_points.txt +3 -0
- devin/__init__.py +6 -0
- devin/dag.py +58 -0
- devin/dag_two_arm.py +138 -0
- devin/devin_chat_scenario_catalog.json +588 -0
- devin/devin_eval.py +677 -0
- devin/nodes/__init__.py +0 -0
- devin/nodes/ideation/__init__.py +0 -0
- devin/nodes/ideation/node.py +195 -0
- devin/nodes/ideation/playground.py +267 -0
- devin/nodes/ideation/prompt.md +65 -0
- devin/nodes/ideation/scenarios/continue_refinement.py +13 -0
- devin/nodes/ideation/scenarios/continue_refinement_evals.py +18 -0
- devin/nodes/ideation/scenarios/idea_fits_existing_patterns.py +17 -0
- devin/nodes/ideation/scenarios/idea_fits_existing_patterns_evals.py +16 -0
- devin/nodes/ideation/scenarios/large_idea_split.py +4 -0
- devin/nodes/ideation/scenarios/large_idea_split_evals.py +17 -0
- devin/nodes/ideation/scenarios/source_documentation_added.py +4 -0
- devin/nodes/ideation/scenarios/source_documentation_added_evals.py +16 -0
- devin/nodes/ideation/scenarios/user_says_create_it.py +30 -0
- devin/nodes/ideation/scenarios/user_says_create_it_evals.py +23 -0
- devin/nodes/ideation/scenarios/vague_idea.py +16 -0
- devin/nodes/ideation/scenarios/vague_idea_evals.py +47 -0
- devin/nodes/ideation/tools.json +312 -0
- devin/nodes/insight/__init__.py +0 -0
- devin/nodes/insight/node.py +49 -0
- devin/nodes/insight/playground.py +154 -0
- devin/nodes/insight/prompt.md +61 -0
- devin/nodes/insight/scenarios/architecture_pattern_query.py +15 -0
- devin/nodes/insight/scenarios/architecture_pattern_query_evals.py +25 -0
- devin/nodes/insight/scenarios/codebase_exploration.py +15 -0
- devin/nodes/insight/scenarios/codebase_exploration_evals.py +23 -0
- devin/nodes/insight/scenarios/devin_ideation_routing.py +19 -0
- devin/nodes/insight/scenarios/devin_ideation_routing_evals.py +39 -0
- devin/nodes/insight/scenarios/devin_insight_routing.py +20 -0
- devin/nodes/insight/scenarios/devin_insight_routing_evals.py +40 -0
- devin/nodes/insight/scenarios/operational_debugging.py +15 -0
- devin/nodes/insight/scenarios/operational_debugging_evals.py +23 -0
- devin/nodes/insight/scenarios/operational_question.py +9 -0
- devin/nodes/insight/scenarios/operational_question_evals.py +8 -0
- devin/nodes/insight/scenarios/queue_status.py +15 -0
- devin/nodes/insight/scenarios/queue_status_evals.py +23 -0
- devin/nodes/insight/scenarios/source_doc_explanation.py +14 -0
- devin/nodes/insight/scenarios/source_doc_explanation_evals.py +21 -0
- devin/nodes/insight/scenarios/worker_state_check.py +15 -0
- devin/nodes/insight/scenarios/worker_state_check_evals.py +22 -0
- devin/nodes/insight/tools.json +126 -0
- devin/nodes/intake/__init__.py +0 -0
- devin/nodes/intake/node.py +27 -0
- devin/nodes/intake/playground.py +47 -0
- devin/nodes/intake/prompt.md +12 -0
- devin/nodes/intake/scenarios/ideation_routing.py +4 -0
- devin/nodes/intake/scenarios/ideation_routing_evals.py +5 -0
- devin/nodes/intake/scenarios/insight_routing.py +4 -0
- devin/nodes/intake/scenarios/insight_routing_evals.py +5 -0
- devin/nodes/iterate/README.md +44 -0
- devin/nodes/iterate/__init__.py +1 -0
- devin/nodes/iterate/_archived_design_stages/01-objectives-requirements.md +112 -0
- devin/nodes/iterate/_archived_design_stages/02-evals.md +131 -0
- devin/nodes/iterate/_archived_design_stages/03-tools-and-boundaries.md +110 -0
- devin/nodes/iterate/_archived_design_stages/04-harness-and-playground.md +32 -0
- devin/nodes/iterate/_archived_design_stages/05-prompt-deferred.md +11 -0
- devin/nodes/iterate/_archived_design_stages/coder_agent_design/01-objectives-requirements.md +20 -0
- devin/nodes/iterate/_archived_design_stages/coder_agent_design/02-evals.md +8 -0
- devin/nodes/iterate/_archived_design_stages/coder_agent_design/03-tools-and-boundaries.md +14 -0
- devin/nodes/iterate/_archived_design_stages/coder_agent_design/04-harness-and-playground.md +12 -0
- devin/nodes/iterate/_archived_design_stages/framer_agent_design/01-objectives-requirements.md +20 -0
- devin/nodes/iterate/_archived_design_stages/framer_agent_design/02-evals.md +8 -0
- devin/nodes/iterate/_archived_design_stages/framer_agent_design/03-tools-and-boundaries.md +13 -0
- devin/nodes/iterate/_archived_design_stages/framer_agent_design/04-harness-and-playground.md +12 -0
- devin/nodes/iterate/_archived_design_stages/iterator_agent_design/01-objectives-requirements.md +25 -0
- devin/nodes/iterate/_archived_design_stages/iterator_agent_design/02-evals.md +9 -0
- devin/nodes/iterate/_archived_design_stages/iterator_agent_design/03-tools-and-boundaries.md +14 -0
- devin/nodes/iterate/_archived_design_stages/iterator_agent_design/04-harness-and-playground.md +12 -0
- devin/nodes/iterate/_archived_design_stages/observer_agent_design/01-objectives-requirements.md +20 -0
- devin/nodes/iterate/_archived_design_stages/observer_agent_design/02-evals.md +8 -0
- devin/nodes/iterate/_archived_design_stages/observer_agent_design/03-tools-and-boundaries.md +14 -0
- devin/nodes/iterate/_archived_design_stages/observer_agent_design/04-harness-and-playground.md +13 -0
- devin/nodes/iterate/agent-roles.md +89 -0
- devin/nodes/iterate/agents/README.md +10 -0
- devin/nodes/iterate/artifacts.md +504 -0
- devin/nodes/iterate/contract.md +100 -0
- devin/nodes/iterate/eval-plan.md +74 -0
- devin/nodes/iterate/node.py +100 -0
- devin/nodes/iterate/pipeline/README.md +13 -0
- devin/nodes/iterate/playground-contract.md +76 -0
- devin/nodes/iterate/prompt.md +11 -0
- devin/nodes/iterate/scenarios/README.md +38 -0
- devin/nodes/iterate/scenarios/artifact-and-loop-scenarios.md +101 -0
- devin/nodes/iterate/scenarios/coder_artifact_alignment.py +32 -0
- devin/nodes/iterate/scenarios/coder_artifact_alignment_evals.py +45 -0
- devin/nodes/iterate/scenarios/coder_bounded_fix.py +27 -0
- devin/nodes/iterate/scenarios/coder_bounded_fix_evals.py +45 -0
- devin/nodes/iterate/scenarios/devin_iterate_routing.py +21 -0
- devin/nodes/iterate/scenarios/devin_iterate_routing_evals.py +36 -0
- devin/nodes/iterate/scenarios/framer_scope_boundary.py +25 -0
- devin/nodes/iterate/scenarios/framer_scope_boundary_evals.py +57 -0
- devin/nodes/iterate/scenarios/framer_task_framing.py +25 -0
- devin/nodes/iterate/scenarios/framer_task_framing_evals.py +58 -0
- devin/nodes/iterate/scenarios/iterate_error_fix.py +21 -0
- devin/nodes/iterate/scenarios/iterate_error_fix_evals.py +39 -0
- devin/nodes/iterate/scenarios/iterate_quick_change.py +21 -0
- devin/nodes/iterate/scenarios/iterate_quick_change_evals.py +35 -0
- devin/nodes/iterate/scenarios/iterate_to_idea_promotion.py +23 -0
- devin/nodes/iterate/scenarios/iterate_to_idea_promotion_evals.py +53 -0
- devin/nodes/iterate/scenarios/iterate_to_insight_reroute.py +23 -0
- devin/nodes/iterate/scenarios/iterate_to_insight_reroute_evals.py +53 -0
- devin/nodes/iterate/scenarios/observer_evidence_seam.py +28 -0
- devin/nodes/iterate/scenarios/observer_evidence_seam_evals.py +55 -0
- devin/nodes/iterate/scenarios/observer_repro_creation.py +28 -0
- devin/nodes/iterate/scenarios/observer_repro_creation_evals.py +45 -0
- devin/nodes/iterate/scenarios/routing-matrix.md +45 -0
- devin/nodes/shared/__init__.py +0 -0
- devin/nodes/shared/filemaker_expert.md +80 -0
- devin/nodes/shared/filemaker_expert.py +354 -0
- devin/nodes/shared/filemaker_expert_eval/runner.py +176 -0
- devin/nodes/shared/filemaker_expert_eval/scenarios.json +65 -0
- devin/nodes/shared/goldilocks_advisor_eval/runner.py +214 -0
- devin/nodes/shared/goldilocks_advisor_eval/scenarios.json +58 -0
- devin/nodes/shared/helpers.py +156 -0
- devin/nodes/shared/idea_compliance_advisor_eval/runner.py +252 -0
- devin/nodes/shared/idea_compliance_advisor_eval/scenarios.json +75 -0
- devin/nodes/shared/models.py +44 -0
- devin/nodes/shared/post.py +40 -0
- devin/nodes/shared/router.py +107 -0
- devin/nodes/shared/tools.py +191 -0
- devin/shared/devin-chat-rubric.md +237 -0
- devin/shared/devin-chat-scenario-suite.md +90 -0
- devin/shared/eval_doctrine.md +9 -0
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import base64
|
|
4
|
+
import binascii
|
|
5
|
+
import hashlib
|
|
6
|
+
import hmac
|
|
7
|
+
import json
|
|
8
|
+
import os
|
|
9
|
+
import subprocess
|
|
10
|
+
import time
|
|
11
|
+
from dataclasses import dataclass
|
|
12
|
+
from datetime import UTC, datetime
|
|
13
|
+
from hashlib import sha256
|
|
14
|
+
from typing import Any
|
|
15
|
+
from urllib.error import HTTPError
|
|
16
|
+
from urllib.parse import quote
|
|
17
|
+
from urllib.request import Request, urlopen
|
|
18
|
+
from uuid import UUID
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
_KEYCHAIN_SERVICE = "devflow-engine.provider-api-key"
|
|
22
|
+
_TRANSPORT_GRANT_PURPOSE = "devflow_settings_transport_wrap"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@dataclass(frozen=True)
|
|
26
|
+
class ProviderSecretSpec:
|
|
27
|
+
provider: str
|
|
28
|
+
env_var: str
|
|
29
|
+
aliases: tuple[str, ...] = ()
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
_PROVIDER_SPECS: tuple[ProviderSecretSpec, ...] = (
|
|
33
|
+
ProviderSecretSpec("anthropic", "ANTHROPIC_API_KEY", aliases=("claude",)),
|
|
34
|
+
ProviderSecretSpec("openai", "OPENAI_API_KEY", aliases=("codex",)),
|
|
35
|
+
ProviderSecretSpec("google", "GOOGLE_API_KEY", aliases=("gemini", "gemini-cli")),
|
|
36
|
+
ProviderSecretSpec("ollama", "OLLAMA_API_KEY"),
|
|
37
|
+
ProviderSecretSpec("openrouter", "OPENROUTER_API_KEY"),
|
|
38
|
+
ProviderSecretSpec("mistral", "MISTRAL_API_KEY"),
|
|
39
|
+
ProviderSecretSpec("deepseek", "DEEPSEEK_API_KEY"),
|
|
40
|
+
ProviderSecretSpec("xai", "XAI_API_KEY", aliases=("grok",)),
|
|
41
|
+
ProviderSecretSpec("minimax", "MINIMAX_API_KEY"),
|
|
42
|
+
)
|
|
43
|
+
_PROVIDER_INDEX = {
|
|
44
|
+
alias: spec
|
|
45
|
+
for spec in _PROVIDER_SPECS
|
|
46
|
+
for alias in (spec.provider, *spec.aliases)
|
|
47
|
+
}
|
|
48
|
+
_RUNTIME_PROVIDER_KEYS: dict[str, str] = {}
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _resolve_provider_spec(provider: str) -> ProviderSecretSpec:
|
|
52
|
+
normalized = str(provider or "").strip().lower()
|
|
53
|
+
if not normalized:
|
|
54
|
+
raise RuntimeError("API-key provider is required")
|
|
55
|
+
spec = _PROVIDER_INDEX.get(normalized)
|
|
56
|
+
if spec is None:
|
|
57
|
+
raise RuntimeError(f"Unsupported API-key provider: {normalized}")
|
|
58
|
+
return spec
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def _run_security_command(args: list[str]) -> subprocess.CompletedProcess[str]:
|
|
62
|
+
return subprocess.run(
|
|
63
|
+
["security", *args],
|
|
64
|
+
capture_output=True,
|
|
65
|
+
text=True,
|
|
66
|
+
check=False,
|
|
67
|
+
timeout=10,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _keychain_get(service: str, account: str) -> str | None:
|
|
72
|
+
try:
|
|
73
|
+
proc = _run_security_command(["find-generic-password", "-s", service, "-a", account, "-w"])
|
|
74
|
+
except Exception:
|
|
75
|
+
return None
|
|
76
|
+
if proc.returncode != 0:
|
|
77
|
+
return None
|
|
78
|
+
value = proc.stdout.strip()
|
|
79
|
+
return value or None
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def load_provider_api_key_from_keychain(provider: str) -> str | None:
|
|
83
|
+
spec = _resolve_provider_spec(provider)
|
|
84
|
+
try:
|
|
85
|
+
proc = _run_security_command(["find-generic-password", "-s", _KEYCHAIN_SERVICE, "-a", spec.provider, "-w"])
|
|
86
|
+
except Exception:
|
|
87
|
+
return None
|
|
88
|
+
if proc.returncode != 0:
|
|
89
|
+
return None
|
|
90
|
+
value = proc.stdout.strip()
|
|
91
|
+
return value or None
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def store_provider_api_key(provider: str, api_key: str) -> None:
|
|
95
|
+
spec = _resolve_provider_spec(provider)
|
|
96
|
+
credential = validate_provider_api_key(provider=spec.provider, api_key=api_key)
|
|
97
|
+
try:
|
|
98
|
+
proc = _run_security_command(
|
|
99
|
+
["add-generic-password", "-U", "-s", _KEYCHAIN_SERVICE, "-a", spec.provider, "-w", credential]
|
|
100
|
+
)
|
|
101
|
+
except Exception as exc:
|
|
102
|
+
raise RuntimeError(f"Failed to write macOS keychain entry for provider {spec.provider}") from exc
|
|
103
|
+
if proc.returncode != 0:
|
|
104
|
+
raise RuntimeError(f"Failed to write macOS keychain entry for provider {spec.provider}")
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def set_runtime_provider_api_key(provider: str, api_key: str, *, env: dict[str, str] | None = None) -> str:
|
|
108
|
+
spec = _resolve_provider_spec(provider)
|
|
109
|
+
credential = validate_provider_api_key(provider=spec.provider, api_key=api_key)
|
|
110
|
+
target_env = os.environ if env is None else env
|
|
111
|
+
target_env[spec.env_var] = credential
|
|
112
|
+
_RUNTIME_PROVIDER_KEYS[spec.provider] = credential
|
|
113
|
+
return spec.env_var
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def get_provider_api_key(provider: str, *, env: dict[str, str] | None = None) -> str | None:
|
|
117
|
+
spec = _resolve_provider_spec(provider)
|
|
118
|
+
target_env = os.environ if env is None else env
|
|
119
|
+
env_value = str(target_env.get(spec.env_var) or "").strip()
|
|
120
|
+
if env_value:
|
|
121
|
+
_RUNTIME_PROVIDER_KEYS[spec.provider] = env_value
|
|
122
|
+
return env_value
|
|
123
|
+
cached = _RUNTIME_PROVIDER_KEYS.get(spec.provider)
|
|
124
|
+
if cached:
|
|
125
|
+
return cached
|
|
126
|
+
stored = load_provider_api_key_from_keychain(spec.provider)
|
|
127
|
+
if stored:
|
|
128
|
+
set_runtime_provider_api_key(spec.provider, stored, env=target_env)
|
|
129
|
+
return stored
|
|
130
|
+
return None
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def bootstrap_provider_api_keys(*, env: dict[str, str] | None = None) -> dict[str, str]:
|
|
134
|
+
loaded: dict[str, str] = {}
|
|
135
|
+
for spec in _PROVIDER_SPECS:
|
|
136
|
+
value = load_provider_api_key_from_keychain(spec.provider)
|
|
137
|
+
if not value:
|
|
138
|
+
continue
|
|
139
|
+
set_runtime_provider_api_key(spec.provider, value, env=env)
|
|
140
|
+
loaded[spec.provider] = spec.env_var
|
|
141
|
+
return loaded
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def validate_provider_api_key(*, provider: str, api_key: str) -> str:
|
|
145
|
+
_resolve_provider_spec(provider)
|
|
146
|
+
credential = str(api_key or "").strip()
|
|
147
|
+
if not credential:
|
|
148
|
+
raise RuntimeError("API-key credential is required")
|
|
149
|
+
if any(token in credential for token in ("\n", "\r", "\x00")):
|
|
150
|
+
raise RuntimeError("API-key credential failed validation")
|
|
151
|
+
if len(credential) < 8:
|
|
152
|
+
raise RuntimeError("API-key credential failed validation")
|
|
153
|
+
return credential
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
class DevflowTransportGrantStore:
|
|
157
|
+
def __init__(self, *, endpoint_url: str, auth_secret: str | None = None):
|
|
158
|
+
self.endpoint_url = endpoint_url.rstrip("/")
|
|
159
|
+
self.auth_secret = str(auth_secret or "").strip() or None
|
|
160
|
+
|
|
161
|
+
@staticmethod
|
|
162
|
+
def from_env() -> "DevflowTransportGrantStore":
|
|
163
|
+
endpoint_url = (
|
|
164
|
+
os.environ.get("DEVFLOW_TRANSPORT_GRANT_RESOLUTION_URL")
|
|
165
|
+
or os.environ.get("CLARITY_TRANSPORT_GRANT_RESOLUTION_URL")
|
|
166
|
+
)
|
|
167
|
+
if not endpoint_url:
|
|
168
|
+
backend_url = os.environ.get("DEVFLOW_BACKEND_URL") or os.environ.get("CLARITY_BACKEND_URL")
|
|
169
|
+
if backend_url:
|
|
170
|
+
normalized_backend_url = backend_url.rstrip("/")
|
|
171
|
+
if normalized_backend_url.endswith("/api"):
|
|
172
|
+
endpoint_url = f"{normalized_backend_url}/devflow/settings/transport-grant"
|
|
173
|
+
else:
|
|
174
|
+
endpoint_url = f"{normalized_backend_url}/api/devflow/settings/transport-grant"
|
|
175
|
+
if not endpoint_url:
|
|
176
|
+
raise RuntimeError("DevFlow transport grant resolution endpoint is not configured")
|
|
177
|
+
auth_secret = (
|
|
178
|
+
os.environ.get("DEVFLOW_TRANSPORT_GRANT_RESOLUTION_SECRET")
|
|
179
|
+
or os.environ.get("CLARITY_SECRET_KEY")
|
|
180
|
+
or os.environ.get("DEVFLOW_BACKEND_SECRET_KEY")
|
|
181
|
+
or os.environ.get("SECRET_KEY")
|
|
182
|
+
)
|
|
183
|
+
return DevflowTransportGrantStore(endpoint_url=endpoint_url, auth_secret=auth_secret)
|
|
184
|
+
|
|
185
|
+
@staticmethod
|
|
186
|
+
def _build_hmac_bearer_token(secret: str, payload: str) -> str:
|
|
187
|
+
timestamp = str(int(time.time()))
|
|
188
|
+
message = f"{timestamp}.{payload}"
|
|
189
|
+
signature = hmac.new(
|
|
190
|
+
secret.encode("utf-8"),
|
|
191
|
+
message.encode("utf-8"),
|
|
192
|
+
hashlib.sha256,
|
|
193
|
+
).hexdigest()
|
|
194
|
+
return f"Bearer {signature}.{timestamp}"
|
|
195
|
+
|
|
196
|
+
def fetch(self, *, grant_id: UUID | str) -> dict[str, Any] | None:
|
|
197
|
+
url = f"{self.endpoint_url}/{quote(str(grant_id), safe='')}"
|
|
198
|
+
req = Request(url, method="GET")
|
|
199
|
+
req.add_header("Accept", "application/json")
|
|
200
|
+
if self.auth_secret:
|
|
201
|
+
req.add_header("Authorization", self._build_hmac_bearer_token(self.auth_secret, ""))
|
|
202
|
+
try:
|
|
203
|
+
with urlopen(req, timeout=30) as resp:
|
|
204
|
+
raw = resp.read().decode("utf-8")
|
|
205
|
+
except HTTPError as exc:
|
|
206
|
+
if exc.code == 404:
|
|
207
|
+
return None
|
|
208
|
+
detail = ""
|
|
209
|
+
try:
|
|
210
|
+
detail = exc.read().decode("utf-8").strip()
|
|
211
|
+
except Exception:
|
|
212
|
+
detail = ""
|
|
213
|
+
message = detail or exc.reason or f"HTTP {exc.code}"
|
|
214
|
+
raise RuntimeError(f"DevFlow transport grant resolution request failed: {message}") from exc
|
|
215
|
+
except Exception as exc:
|
|
216
|
+
raise RuntimeError("DevFlow transport grant resolution request failed") from exc
|
|
217
|
+
if not raw:
|
|
218
|
+
return None
|
|
219
|
+
try:
|
|
220
|
+
payload = json.loads(raw)
|
|
221
|
+
except json.JSONDecodeError:
|
|
222
|
+
return None
|
|
223
|
+
if not isinstance(payload, dict):
|
|
224
|
+
return None
|
|
225
|
+
normalized = dict(payload)
|
|
226
|
+
aliases = {
|
|
227
|
+
"grantId": "grant_id",
|
|
228
|
+
"grantToken": "grant_token",
|
|
229
|
+
"wrappedKey": "wrapped_key",
|
|
230
|
+
"wrappingAlgorithm": "wrapping_algorithm",
|
|
231
|
+
"wrappingKeyId": "wrapping_key_id",
|
|
232
|
+
"expiresAt": "expires_at",
|
|
233
|
+
}
|
|
234
|
+
for source_key, target_key in aliases.items():
|
|
235
|
+
if source_key in normalized:
|
|
236
|
+
normalized.setdefault(target_key, normalized.pop(source_key))
|
|
237
|
+
if normalized.get("grant_token") and "purpose" not in normalized:
|
|
238
|
+
normalized["purpose"] = _TRANSPORT_GRANT_PURPOSE
|
|
239
|
+
grant = normalized.get("grant")
|
|
240
|
+
if isinstance(grant, dict):
|
|
241
|
+
return grant
|
|
242
|
+
return normalized
|
|
243
|
+
|
|
244
|
+
def close(self) -> None:
|
|
245
|
+
return None
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def resolve_transport_grant(*, grant_id: str, grant_store: DevflowTransportGrantStore | None = None) -> dict[str, Any]:
|
|
249
|
+
own_store = grant_store is None
|
|
250
|
+
store = grant_store or DevflowTransportGrantStore.from_env()
|
|
251
|
+
try:
|
|
252
|
+
payload = store.fetch(grant_id=grant_id)
|
|
253
|
+
finally:
|
|
254
|
+
if own_store:
|
|
255
|
+
store.close()
|
|
256
|
+
if not isinstance(payload, dict):
|
|
257
|
+
raise RuntimeError("DevFlow transport grant is unavailable or expired")
|
|
258
|
+
if str(payload.get("purpose") or "") != _TRANSPORT_GRANT_PURPOSE:
|
|
259
|
+
raise RuntimeError("DevFlow transport grant purpose is invalid")
|
|
260
|
+
grant_token = str(payload.get("grant_token") or "").strip()
|
|
261
|
+
if not grant_token:
|
|
262
|
+
raise RuntimeError("DevFlow transport grant is missing unwrap material")
|
|
263
|
+
expires_at = str(payload.get("expires_at") or "").strip()
|
|
264
|
+
if expires_at:
|
|
265
|
+
try:
|
|
266
|
+
expires_dt = datetime.fromisoformat(expires_at.replace("Z", "+00:00"))
|
|
267
|
+
except ValueError as exc:
|
|
268
|
+
raise RuntimeError("DevFlow transport grant metadata is invalid") from exc
|
|
269
|
+
if expires_dt.astimezone(UTC) <= datetime.now(UTC):
|
|
270
|
+
raise RuntimeError("DevFlow transport grant is unavailable or expired")
|
|
271
|
+
return payload
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def decrypt_transport_payload(*, grant_token: str, transport_payload: dict[str, Any]) -> dict[str, Any]:
|
|
275
|
+
try:
|
|
276
|
+
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
|
|
277
|
+
except ImportError as exc:
|
|
278
|
+
raise RuntimeError("cryptography package is required for DevFlow API-key transport unwrap") from exc
|
|
279
|
+
|
|
280
|
+
ciphertext_b64 = str(transport_payload.get("ciphertext") or "")
|
|
281
|
+
iv_b64 = str(transport_payload.get("iv") or "")
|
|
282
|
+
metadata = transport_payload.get("metadata") or {}
|
|
283
|
+
if not ciphertext_b64 or not iv_b64 or not isinstance(metadata, dict):
|
|
284
|
+
raise RuntimeError("DevFlow API-key transport payload is incomplete")
|
|
285
|
+
if str(metadata.get("algorithm") or "") != "AES-GCM":
|
|
286
|
+
raise RuntimeError("Unsupported DevFlow transport algorithm")
|
|
287
|
+
if str(metadata.get("key_derivation") or "") != "SHA-256":
|
|
288
|
+
raise RuntimeError("Unsupported DevFlow transport key derivation")
|
|
289
|
+
if str(metadata.get("key_material") or "") != "transport-grant-token":
|
|
290
|
+
raise RuntimeError("Unsupported DevFlow transport key material")
|
|
291
|
+
if str(metadata.get("version") or "") != "devflow.api_key.v1":
|
|
292
|
+
raise RuntimeError("Unsupported DevFlow transport payload version")
|
|
293
|
+
|
|
294
|
+
try:
|
|
295
|
+
ciphertext = base64.b64decode(ciphertext_b64, validate=True)
|
|
296
|
+
iv = base64.b64decode(iv_b64, validate=True)
|
|
297
|
+
except (ValueError, binascii.Error) as exc:
|
|
298
|
+
raise RuntimeError("DevFlow API-key transport payload is malformed") from exc
|
|
299
|
+
|
|
300
|
+
key = sha256(grant_token.encode("utf-8")).digest()
|
|
301
|
+
try:
|
|
302
|
+
plaintext = AESGCM(key).decrypt(iv, ciphertext, None)
|
|
303
|
+
except Exception as exc:
|
|
304
|
+
raise RuntimeError("Unable to decrypt DevFlow API-key transport payload") from exc
|
|
305
|
+
|
|
306
|
+
try:
|
|
307
|
+
payload = json.loads(plaintext.decode("utf-8"))
|
|
308
|
+
except (UnicodeDecodeError, json.JSONDecodeError) as exc:
|
|
309
|
+
raise RuntimeError("Decrypted DevFlow API-key transport payload is invalid") from exc
|
|
310
|
+
if not isinstance(payload, dict):
|
|
311
|
+
raise RuntimeError("Decrypted DevFlow API-key transport payload is invalid")
|
|
312
|
+
return payload
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def unwrap_api_key_event_payload(
|
|
316
|
+
*,
|
|
317
|
+
payload: dict[str, Any],
|
|
318
|
+
grant_store: DevflowTransportGrantStore | None = None,
|
|
319
|
+
) -> dict[str, str | None]:
|
|
320
|
+
transport = payload.get("transport")
|
|
321
|
+
transport_payload = payload.get("transport_payload")
|
|
322
|
+
if not isinstance(transport, dict):
|
|
323
|
+
raise RuntimeError("devflow_API_KEY requires payload.transport object")
|
|
324
|
+
if not isinstance(transport_payload, dict):
|
|
325
|
+
raise RuntimeError("devflow_API_KEY requires payload.transport_payload object")
|
|
326
|
+
|
|
327
|
+
grant_id = str(transport.get("grant_id") or "").strip()
|
|
328
|
+
if not grant_id:
|
|
329
|
+
raise RuntimeError("devflow_API_KEY requires payload.transport.grant_id")
|
|
330
|
+
grant = resolve_transport_grant(grant_id=grant_id, grant_store=grant_store)
|
|
331
|
+
decrypted = decrypt_transport_payload(grant_token=str(grant["grant_token"]), transport_payload=transport_payload)
|
|
332
|
+
|
|
333
|
+
secret = decrypted.get("secret")
|
|
334
|
+
if not isinstance(secret, dict):
|
|
335
|
+
raise RuntimeError("Decrypted DevFlow API-key payload is missing secret material")
|
|
336
|
+
provider_spec = _resolve_provider_spec(str(secret.get("provider") or ""))
|
|
337
|
+
credential = validate_provider_api_key(provider=provider_spec.provider, api_key=str(secret.get("credential") or ""))
|
|
338
|
+
tier = str(secret.get("tier") or "").strip() or None
|
|
339
|
+
|
|
340
|
+
descriptor = payload.get("secret_descriptor")
|
|
341
|
+
if isinstance(descriptor, dict):
|
|
342
|
+
descriptor_provider = str(descriptor.get("provider") or "").strip().lower()
|
|
343
|
+
if descriptor_provider and _resolve_provider_spec(descriptor_provider).provider != provider_spec.provider:
|
|
344
|
+
raise RuntimeError("devflow_API_KEY provider descriptor mismatch")
|
|
345
|
+
descriptor_last4 = descriptor.get("last4")
|
|
346
|
+
if descriptor_last4 is not None and str(descriptor_last4) != credential[-4:]:
|
|
347
|
+
raise RuntimeError("devflow_API_KEY credential descriptor mismatch")
|
|
348
|
+
descriptor_length = descriptor.get("length")
|
|
349
|
+
if descriptor_length is not None and int(descriptor_length) != len(credential):
|
|
350
|
+
raise RuntimeError("devflow_API_KEY credential descriptor mismatch")
|
|
351
|
+
|
|
352
|
+
return {
|
|
353
|
+
"provider": provider_spec.provider,
|
|
354
|
+
"api_key": credential,
|
|
355
|
+
"tier": tier,
|
|
356
|
+
"env_var": provider_spec.env_var,
|
|
357
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
import re
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
CLARIFY_COMMAND_REFERENCE = (
|
|
9
|
+
"python3 <(gh api repos/Nuosis/clarify/contents/scripts/provision_from_template.py "
|
|
10
|
+
"--jq '.content' | base64 -d)"
|
|
11
|
+
)
|
|
12
|
+
COPY_PROVISION_STRATEGY = "COPY_PROVISION_SCRIPT"
|
|
13
|
+
LOCAL_ONLY_UNTIL_APPROVED = "LOCAL_ONLY_UNTIL_APPROVED"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def _write(path: Path, text: str) -> None:
|
|
17
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
18
|
+
path.write_text(text, encoding="utf-8")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _slug(value: str) -> str:
|
|
22
|
+
raw = re.sub(r"[^a-z0-9]+", "-", value.strip().lower()).strip("-")
|
|
23
|
+
return raw or "python-backend"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _pkg_name(value: str) -> str:
|
|
27
|
+
raw = re.sub(r"[^a-z0-9]+", "_", value.strip().lower()).strip("_")
|
|
28
|
+
return raw or "python_backend"
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def main(argv: list[str] | None = None) -> int:
|
|
32
|
+
parser = argparse.ArgumentParser(description="Provision a deterministic Python backend scaffold.")
|
|
33
|
+
parser.add_argument("--dest", required=True, type=Path)
|
|
34
|
+
parser.add_argument("--project-name", required=True)
|
|
35
|
+
args = parser.parse_args(argv)
|
|
36
|
+
|
|
37
|
+
dest = args.dest.resolve()
|
|
38
|
+
project_name = args.project_name.strip() or "Python Backend"
|
|
39
|
+
slug = _slug(project_name)
|
|
40
|
+
package_name = _pkg_name(project_name)
|
|
41
|
+
|
|
42
|
+
_write(dest / "README.md", f"# {project_name}\n\nProvisioned via Clarify COPY/provision-script semantics.\n")
|
|
43
|
+
_write(
|
|
44
|
+
dest / "pyproject.toml",
|
|
45
|
+
"\n".join(
|
|
46
|
+
[
|
|
47
|
+
"[project]",
|
|
48
|
+
f'name = "{slug}"',
|
|
49
|
+
'version = "0.1.0"',
|
|
50
|
+
'requires-python = ">=3.11"',
|
|
51
|
+
"",
|
|
52
|
+
"[build-system]",
|
|
53
|
+
'requires = ["hatchling"]',
|
|
54
|
+
'build-backend = "hatchling.build"',
|
|
55
|
+
"",
|
|
56
|
+
]
|
|
57
|
+
),
|
|
58
|
+
)
|
|
59
|
+
_write(dest / "src" / package_name / "__init__.py", "__all__ = []\n")
|
|
60
|
+
_write(
|
|
61
|
+
dest / "src" / package_name / "app.py",
|
|
62
|
+
"def healthcheck() -> dict[str, str]:\n"
|
|
63
|
+
" return {\"status\": \"ok\"}\n",
|
|
64
|
+
)
|
|
65
|
+
_write(
|
|
66
|
+
dest / ".devflow-backend-bootstrap.json",
|
|
67
|
+
json.dumps(
|
|
68
|
+
{
|
|
69
|
+
"provisioning_strategy": COPY_PROVISION_STRATEGY,
|
|
70
|
+
"clarify_command_reference": CLARIFY_COMMAND_REFERENCE,
|
|
71
|
+
"backend_default_deploy_behavior": LOCAL_ONLY_UNTIL_APPROVED,
|
|
72
|
+
"project_name": project_name,
|
|
73
|
+
"package_name": package_name,
|
|
74
|
+
},
|
|
75
|
+
indent=2,
|
|
76
|
+
sort_keys=True,
|
|
77
|
+
)
|
|
78
|
+
+ "\n",
|
|
79
|
+
)
|
|
80
|
+
return 0
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
if __name__ == "__main__":
|
|
84
|
+
raise SystemExit(main())
|
|
File without changes
|