orizu 0.5.19 → 0.5.21
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/README.md +23 -0
- package/dist/archive-artifacts-cli.d.ts +50 -0
- package/dist/archive-artifacts-cli.d.ts.map +1 -0
- package/dist/artifact-archive.d.ts +48 -0
- package/dist/artifact-archive.d.ts.map +1 -0
- package/dist/artifact-pull.d.ts +22 -0
- package/dist/artifact-pull.d.ts.map +1 -0
- package/dist/banner.d.ts +3 -0
- package/dist/banner.d.ts.map +1 -0
- package/dist/connectors-cli.d.ts +31 -0
- package/dist/connectors-cli.d.ts.map +1 -0
- package/dist/credentials.d.ts +46 -0
- package/dist/credentials.d.ts.map +1 -0
- package/dist/csv.d.ts +49 -0
- package/dist/csv.d.ts.map +1 -0
- package/dist/dataset-download.d.ts +6 -0
- package/dist/dataset-download.d.ts.map +1 -0
- package/dist/dataset-edit-rows.d.ts +10 -0
- package/dist/dataset-edit-rows.d.ts.map +1 -0
- package/dist/dataset-upload-snapshot.d.ts +13 -0
- package/dist/dataset-upload-snapshot.d.ts.map +1 -0
- package/dist/daytona-slice-rehearsal.d.ts +89 -0
- package/dist/daytona-slice-rehearsal.d.ts.map +1 -0
- package/dist/daytona-slice.d.ts +106 -0
- package/dist/daytona-slice.d.ts.map +1 -0
- package/dist/deploy-key-merge-core.d.ts +121 -0
- package/dist/deploy-key-merge-core.d.ts.map +1 -0
- package/dist/diff-comments-cli.d.ts +12 -0
- package/dist/diff-comments-cli.d.ts.map +1 -0
- package/dist/diff-comments-cli.js +21 -2
- package/dist/egress-allowlist-cli.d.ts +23 -0
- package/dist/egress-allowlist-cli.d.ts.map +1 -0
- package/dist/egress-policy.d.ts +108 -0
- package/dist/egress-policy.d.ts.map +1 -0
- package/dist/error-response.d.ts +9 -0
- package/dist/error-response.d.ts.map +1 -0
- package/dist/file-parser.d.ts +5 -0
- package/dist/file-parser.d.ts.map +1 -0
- package/dist/gepa-engine-dispatch.d.ts +9 -0
- package/dist/gepa-engine-dispatch.d.ts.map +1 -0
- package/dist/gepa-engine-dispatch.js +307 -0
- package/dist/gepa-python-command.d.ts +6 -0
- package/dist/gepa-python-command.d.ts.map +1 -0
- package/dist/gepa-python-command.js +26 -0
- package/dist/gepa-python-paths.d.ts +6 -0
- package/dist/gepa-python-paths.d.ts.map +1 -0
- package/dist/gepa-python-paths.js +42 -0
- package/dist/git-credential.d.ts +69 -0
- package/dist/git-credential.d.ts.map +1 -0
- package/dist/git-recovery-environment.d.ts +31 -0
- package/dist/git-recovery-environment.d.ts.map +1 -0
- package/dist/github-setup.d.ts +122 -0
- package/dist/github-setup.d.ts.map +1 -0
- package/dist/global-flags.d.ts +12 -0
- package/dist/global-flags.d.ts.map +1 -0
- package/dist/help.d.ts +46 -0
- package/dist/help.d.ts.map +1 -0
- package/dist/help.js +93 -25
- package/dist/hosted-boot.d.ts +273 -0
- package/dist/hosted-boot.d.ts.map +1 -0
- package/dist/hosted-bootstrap-rehearsal.d.ts +86 -0
- package/dist/hosted-bootstrap-rehearsal.d.ts.map +1 -0
- package/dist/hosted-bootstrap.d.ts +188 -0
- package/dist/hosted-bootstrap.d.ts.map +1 -0
- package/dist/hosted-broker-server.d.ts +50 -0
- package/dist/hosted-broker-server.d.ts.map +1 -0
- package/dist/hosted-git-http-server.d.ts +36 -0
- package/dist/hosted-git-http-server.d.ts.map +1 -0
- package/dist/hosted-harness-claude.d.ts +195 -0
- package/dist/hosted-harness-claude.d.ts.map +1 -0
- package/dist/hosted-harness-opencode.d.ts +227 -0
- package/dist/hosted-harness-opencode.d.ts.map +1 -0
- package/dist/hosted-harness.d.ts +118 -0
- package/dist/hosted-harness.d.ts.map +1 -0
- package/dist/hosted-harvest.d.ts +68 -0
- package/dist/hosted-harvest.d.ts.map +1 -0
- package/dist/hosted-headless.d.ts +72 -0
- package/dist/hosted-headless.d.ts.map +1 -0
- package/dist/hosted-loop-lifecycle.d.ts +206 -0
- package/dist/hosted-loop-lifecycle.d.ts.map +1 -0
- package/dist/hosted-loop.d.ts +170 -0
- package/dist/hosted-loop.d.ts.map +1 -0
- package/dist/hosted-run-event-sink.d.ts +222 -0
- package/dist/hosted-run-event-sink.d.ts.map +1 -0
- package/dist/hosted-runtime-assets.d.ts +186 -0
- package/dist/hosted-runtime-assets.d.ts.map +1 -0
- package/dist/hosted-session-cli.d.ts +258 -0
- package/dist/hosted-session-cli.d.ts.map +1 -0
- package/dist/hosted-skill-staging.d.ts +73 -0
- package/dist/hosted-skill-staging.d.ts.map +1 -0
- package/dist/http.d.ts +8 -0
- package/dist/http.d.ts.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +119 -129
- package/dist/instruction-set-gepa-launch.d.ts +3 -0
- package/dist/instruction-set-gepa-launch.d.ts.map +1 -0
- package/dist/instruction-set-gepa-launch.js +15 -0
- package/dist/instruction-set-loader.d.ts +6 -0
- package/dist/instruction-set-loader.d.ts.map +1 -0
- package/dist/instruction-set-loader.js +59 -0
- package/dist/instruction-set-manifest.d.ts +14 -0
- package/dist/instruction-set-manifest.d.ts.map +1 -0
- package/dist/instruction-set-manifest.js +162 -0
- package/dist/instruction-sets-cli.d.ts +40 -0
- package/dist/instruction-sets-cli.d.ts.map +1 -0
- package/dist/instruction-sets-cli.js +361 -0
- package/dist/json-response.d.ts +4 -0
- package/dist/json-response.d.ts.map +1 -0
- package/dist/jsonl-stream.d.ts +8 -0
- package/dist/jsonl-stream.d.ts.map +1 -0
- package/dist/kill-switch-cli.d.ts +30 -0
- package/dist/kill-switch-cli.d.ts.map +1 -0
- package/dist/loopback-serve.d.ts +25 -0
- package/dist/loopback-serve.d.ts.map +1 -0
- package/dist/manifests-cli.d.ts +22 -0
- package/dist/manifests-cli.d.ts.map +1 -0
- package/dist/markdown-report-input.d.ts +7 -0
- package/dist/markdown-report-input.d.ts.map +1 -0
- package/dist/markdown-report-input.js +15 -1
- package/dist/merge-job.d.ts +155 -0
- package/dist/merge-job.d.ts.map +1 -0
- package/dist/model-configs-cli.d.ts +19 -0
- package/dist/model-configs-cli.d.ts.map +1 -0
- package/dist/model-configs-cli.js +104 -0
- package/dist/optimization-export-cli.d.ts +19 -0
- package/dist/optimization-export-cli.d.ts.map +1 -0
- package/dist/optimizations-list-cli.d.ts +20 -0
- package/dist/optimizations-list-cli.d.ts.map +1 -0
- package/dist/optimizations-promote-cli.d.ts +8 -0
- package/dist/optimizations-promote-cli.d.ts.map +1 -0
- package/dist/optimizations-promote-cli.js +34 -0
- package/dist/option-validation.d.ts +15 -0
- package/dist/option-validation.d.ts.map +1 -0
- package/dist/preview-runtime.d.ts +63 -0
- package/dist/preview-runtime.d.ts.map +1 -0
- package/dist/prompt-draft-push.d.ts +60 -0
- package/dist/prompt-draft-push.d.ts.map +1 -0
- package/dist/prompt-label-cli.d.ts +11 -0
- package/dist/prompt-label-cli.d.ts.map +1 -0
- package/dist/prompt-label-cli.js +29 -0
- package/dist/prompt-length-wire.d.ts +28 -0
- package/dist/prompt-length-wire.d.ts.map +1 -0
- package/dist/prompt-length-wire.js +2 -2
- package/dist/prompt-report-cli.d.ts +10 -0
- package/dist/prompt-report-cli.d.ts.map +1 -0
- package/dist/prompt-report-cli.js +2 -2
- package/dist/prompt-summary-table.d.ts +15 -0
- package/dist/prompt-summary-table.d.ts.map +1 -0
- package/dist/prompt-summary-table.js +6 -0
- package/dist/registration-tag-warning.d.ts +32 -0
- package/dist/registration-tag-warning.d.ts.map +1 -0
- package/dist/report-comments-cli.d.ts +16 -0
- package/dist/report-comments-cli.d.ts.map +1 -0
- package/dist/report-markdown-blankness.d.ts +2 -0
- package/dist/report-markdown-blankness.d.ts.map +1 -0
- package/dist/report-markdown-blankness.js +6 -0
- package/dist/report-replacement-warning.d.ts +17 -0
- package/dist/report-replacement-warning.d.ts.map +1 -0
- package/dist/report-replacement-warning.js +31 -0
- package/dist/repository-backup-core.d.ts +225 -0
- package/dist/repository-backup-core.d.ts.map +1 -0
- package/dist/repository-recovery-manifest.d.ts +83 -0
- package/dist/repository-recovery-manifest.d.ts.map +1 -0
- package/dist/run-event-digest.d.ts +74 -0
- package/dist/run-event-digest.d.ts.map +1 -0
- package/dist/runner-dir-verify.d.ts +66 -0
- package/dist/runner-dir-verify.d.ts.map +1 -0
- package/dist/runner-instruction-set-contract.d.ts +24 -0
- package/dist/runner-instruction-set-contract.d.ts.map +1 -0
- package/dist/runner-instruction-set-contract.js +50 -0
- package/dist/sandbox-hygiene.d.ts +45 -0
- package/dist/sandbox-hygiene.d.ts.map +1 -0
- package/dist/sandbox-provider.d.ts +193 -0
- package/dist/sandbox-provider.d.ts.map +1 -0
- package/dist/scorer-draft-push.d.ts +50 -0
- package/dist/scorer-draft-push.d.ts.map +1 -0
- package/dist/scorers-cli.d.ts +20 -0
- package/dist/scorers-cli.d.ts.map +1 -0
- package/dist/scorers-cli.js +22 -0
- package/dist/scorers-verify-parity-bridge.d.ts +16 -0
- package/dist/scorers-verify-parity-bridge.d.ts.map +1 -0
- package/dist/scorers-verify-parity-bridge.js +90 -0
- package/dist/scorers-verify-parity.d.ts +81 -0
- package/dist/scorers-verify-parity.d.ts.map +1 -0
- package/dist/scorers-verify-parity.js +502 -0
- package/dist/scores-accept-cli.d.ts +21 -0
- package/dist/scores-accept-cli.d.ts.map +1 -0
- package/dist/secret-redaction.d.ts +47 -0
- package/dist/secret-redaction.d.ts.map +1 -0
- package/dist/session-timeline.d.ts +54 -0
- package/dist/session-timeline.d.ts.map +1 -0
- package/dist/setup-prompt.d.ts +5 -0
- package/dist/setup-prompt.d.ts.map +1 -0
- package/dist/skill-installer.d.ts +68 -0
- package/dist/skill-installer.d.ts.map +1 -0
- package/dist/skilled-proposer-launch.d.ts +7 -0
- package/dist/skilled-proposer-launch.d.ts.map +1 -0
- package/dist/skilled-proposer-launch.js +60 -0
- package/dist/smoke.d.ts +102 -0
- package/dist/smoke.d.ts.map +1 -0
- package/dist/start-intent.d.ts +12 -0
- package/dist/start-intent.d.ts.map +1 -0
- package/dist/task-assignment-manifest.d.ts +15 -0
- package/dist/task-assignment-manifest.d.ts.map +1 -0
- package/dist/task-create-error.d.ts +18 -0
- package/dist/task-create-error.d.ts.map +1 -0
- package/dist/team-connectors-cli.d.ts +29 -0
- package/dist/team-connectors-cli.d.ts.map +1 -0
- package/dist/types.d.ts +39 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/vercel-sandbox-provider.d.ts +156 -0
- package/dist/vercel-sandbox-provider.d.ts.map +1 -0
- package/dist/workbench-cli.d.ts +233 -0
- package/dist/workbench-cli.d.ts.map +1 -0
- package/dist/workspace-sync.d.ts +207 -0
- package/dist/workspace-sync.d.ts.map +1 -0
- package/dist/workspace.d.ts +92 -0
- package/dist/workspace.d.ts.map +1 -0
- package/dist/zip-draft-push.d.ts +55 -0
- package/dist/zip-draft-push.d.ts.map +1 -0
- package/package.json +15 -2
- package/requirements/README.md +138 -0
- package/requirements/skilled-proposer.in +66 -0
- package/requirements/skilled-proposer.lock +265 -0
- package/scripts/build-cli-dist.mjs +30 -0
- package/scripts/ensure-skilled-proposer-venv.mjs +5 -0
- package/scripts/orizu-source-checkout.d.mts +1 -0
- package/scripts/orizu-source-checkout.mjs +10 -0
- package/scripts/refresh-skilled-proposer-lock.mjs +256 -0
- package/scripts/vendor-gepa-python.mjs +187 -4
- package/src/diff-comments-cli.ts +41 -3
- package/src/gepa-engine-dispatch.ts +340 -0
- package/src/gepa-python-command.ts +31 -0
- package/src/gepa-python-paths.ts +56 -0
- package/src/help.ts +95 -25
- package/src/index.ts +131 -166
- package/src/instruction-set-gepa-launch.ts +28 -0
- package/src/instruction-set-loader.ts +37 -0
- package/src/instruction-set-manifest.ts +120 -0
- package/src/instruction-sets-cli.ts +306 -0
- package/src/markdown-report-input.ts +19 -1
- package/src/model-configs-cli.ts +119 -0
- package/src/optimizations-promote-cli.ts +41 -0
- package/src/prompt-label-cli.ts +55 -0
- package/src/prompt-length-wire.ts +1 -1
- package/src/prompt-report-cli.ts +2 -2
- package/src/prompt-summary-table.ts +12 -0
- package/src/report-markdown-blankness.ts +6 -0
- package/src/report-replacement-warning.ts +35 -0
- package/src/runner-instruction-set-contract.ts +64 -0
- package/src/scorers-cli.ts +40 -0
- package/src/scorers-verify-parity-bridge.ts +90 -0
- package/src/scorers-verify-parity.ts +564 -0
- package/src/skilled-proposer-launch.ts +89 -0
- package/src/skilled-proposer-venv-manager.mjs +757 -0
- package/tsconfig.json +2 -0
- package/vendor/gepa-python/LICENSE +7 -0
- package/vendor/gepa-python/manifest.json +107 -0
- package/vendor/gepa-python/src/gepa/__init__.py +22 -0
- package/vendor/gepa-python/src/gepa/adapters/__init__.py +0 -0
- package/vendor/gepa-python/src/gepa/adapters/anymaths_adapter/__init__.py +1 -0
- package/vendor/gepa-python/src/gepa/adapters/anymaths_adapter/anymaths_adapter.py +174 -0
- package/vendor/gepa-python/src/gepa/adapters/confidence_adapter/__init__.py +26 -0
- package/vendor/gepa-python/src/gepa/adapters/confidence_adapter/confidence_adapter.py +618 -0
- package/vendor/gepa-python/src/gepa/adapters/confidence_adapter/scoring.py +206 -0
- package/vendor/gepa-python/src/gepa/adapters/default_adapter/__init__.py +0 -0
- package/vendor/gepa-python/src/gepa/adapters/default_adapter/default_adapter.py +206 -0
- package/vendor/gepa-python/src/gepa/adapters/dspy_adapter/__init__.py +0 -0
- package/vendor/gepa-python/src/gepa/adapters/dspy_adapter/dspy_adapter.py +525 -0
- package/vendor/gepa-python/src/gepa/adapters/dspy_adapter/instruction_proposal.py +500 -0
- package/vendor/gepa-python/src/gepa/adapters/dspy_full_program_adapter/__init__.py +6 -0
- package/vendor/gepa-python/src/gepa/adapters/dspy_full_program_adapter/dspy_program_proposal_signature.py +137 -0
- package/vendor/gepa-python/src/gepa/adapters/dspy_full_program_adapter/full_program_adapter.py +269 -0
- package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/__init__.py +56 -0
- package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/evaluation_metrics.py +226 -0
- package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/generic_rag_adapter.py +496 -0
- package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/rag_pipeline.py +238 -0
- package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_store_interface.py +212 -0
- package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/__init__.py +2 -0
- package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/chroma_store.py +196 -0
- package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/lancedb_store.py +422 -0
- package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/milvus_store.py +409 -0
- package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/qdrant_store.py +368 -0
- package/vendor/gepa-python/src/gepa/adapters/generic_rag_adapter/vector_stores/weaviate_store.py +418 -0
- package/vendor/gepa-python/src/gepa/adapters/langchain_adapter/__init__.py +11 -0
- package/vendor/gepa-python/src/gepa/adapters/langchain_adapter/langchain_adapter.py +261 -0
- package/vendor/gepa-python/src/gepa/adapters/mcp_adapter/__init__.py +37 -0
- package/vendor/gepa-python/src/gepa/adapters/mcp_adapter/mcp_adapter.py +693 -0
- package/vendor/gepa-python/src/gepa/adapters/mcp_adapter/mcp_client.py +364 -0
- package/vendor/gepa-python/src/gepa/adapters/optimize_anything_adapter/optimize_anything_adapter.py +879 -0
- package/vendor/gepa-python/src/gepa/adapters/terminal_bench_adapter/__init__.py +0 -0
- package/vendor/gepa-python/src/gepa/adapters/terminal_bench_adapter/terminal_bench_adapter.py +217 -0
- package/vendor/gepa-python/src/gepa/api.py +468 -0
- package/vendor/gepa-python/src/gepa/core/__init__.py +0 -0
- package/vendor/gepa-python/src/gepa/core/adapter.py +227 -0
- package/vendor/gepa-python/src/gepa/core/callbacks.py +551 -0
- package/vendor/gepa-python/src/gepa/core/data_loader.py +74 -0
- package/vendor/gepa-python/src/gepa/core/engine.py +1025 -0
- package/vendor/gepa-python/src/gepa/core/result.py +271 -0
- package/vendor/gepa-python/src/gepa/core/state.py +706 -0
- package/vendor/gepa-python/src/gepa/examples/__init__.py +0 -0
- package/vendor/gepa-python/src/gepa/examples/aime.py +24 -0
- package/vendor/gepa-python/src/gepa/examples/anymaths-bench/eval_default.py +111 -0
- package/vendor/gepa-python/src/gepa/examples/anymaths-bench/train_anymaths.py +177 -0
- package/vendor/gepa-python/src/gepa/examples/mcp_adapter/__init__.py +4 -0
- package/vendor/gepa-python/src/gepa/examples/mcp_adapter/mcp_optimization_example.py +456 -0
- package/vendor/gepa-python/src/gepa/examples/rag_adapter/__init__.py +9 -0
- package/vendor/gepa-python/src/gepa/examples/rag_adapter/rag_optimization.py +824 -0
- package/vendor/gepa-python/src/gepa/examples/terminal-bench/train_terminus.py +161 -0
- package/vendor/gepa-python/src/gepa/gepa_utils.py +117 -0
- package/vendor/gepa-python/src/gepa/gskill/__init__.py +1 -0
- package/vendor/gepa-python/src/gepa/gskill/gskill/__init__.py +0 -0
- package/vendor/gepa-python/src/gepa/gskill/gskill/cost_tracker.py +194 -0
- package/vendor/gepa-python/src/gepa/gskill/gskill/evaluate/__init__.py +0 -0
- package/vendor/gepa-python/src/gepa/gskill/gskill/evaluate/claude_code.py +1117 -0
- package/vendor/gepa-python/src/gepa/gskill/gskill/evaluate/claude_code_skills.py +613 -0
- package/vendor/gepa-python/src/gepa/gskill/gskill/evaluate/mini_swe_agent.py +440 -0
- package/vendor/gepa-python/src/gepa/gskill/gskill/experiment_logger.py +367 -0
- package/vendor/gepa-python/src/gepa/gskill/gskill/swe_fitness_fn.py +209 -0
- package/vendor/gepa-python/src/gepa/gskill/gskill/swe_harness.py +352 -0
- package/vendor/gepa-python/src/gepa/gskill/gskill/train_optimize_anything.py +830 -0
- package/vendor/gepa-python/src/gepa/image.py +104 -0
- package/vendor/gepa-python/src/gepa/lm.py +261 -0
- package/vendor/gepa-python/src/gepa/logging/__init__.py +0 -0
- package/vendor/gepa-python/src/gepa/logging/experiment_tracker.py +457 -0
- package/vendor/gepa-python/src/gepa/logging/logger.py +75 -0
- package/vendor/gepa-python/src/gepa/logging/utils.py +130 -0
- package/vendor/gepa-python/src/gepa/optimize_anything.py +1701 -0
- package/vendor/gepa-python/src/gepa/proposer/__init__.py +0 -0
- package/vendor/gepa-python/src/gepa/proposer/base.py +53 -0
- package/vendor/gepa-python/src/gepa/proposer/merge.py +403 -0
- package/vendor/gepa-python/src/gepa/proposer/reflective_mutation/__init__.py +0 -0
- package/vendor/gepa-python/src/gepa/proposer/reflective_mutation/base.py +64 -0
- package/vendor/gepa-python/src/gepa/proposer/reflective_mutation/reflection_lm.py +178 -0
- package/vendor/gepa-python/src/gepa/proposer/reflective_mutation/reflective_mutation.py +641 -0
- package/vendor/gepa-python/src/gepa/py.typed +0 -0
- package/vendor/gepa-python/src/gepa/strategies/__init__.py +0 -0
- package/vendor/gepa-python/src/gepa/strategies/acceptance.py +66 -0
- package/vendor/gepa-python/src/gepa/strategies/batch_sampler.py +103 -0
- package/vendor/gepa-python/src/gepa/strategies/candidate_selector.py +82 -0
- package/vendor/gepa-python/src/gepa/strategies/component_selector.py +36 -0
- package/vendor/gepa-python/src/gepa/strategies/eval_policy.py +64 -0
- package/vendor/gepa-python/src/gepa/strategies/instruction_proposal.py +153 -0
- package/vendor/gepa-python/src/gepa/strategies/proposal_sampling.py +128 -0
- package/vendor/gepa-python/src/gepa/strategies/proposal_selection.py +84 -0
- package/vendor/gepa-python/src/gepa/utils/__init__.py +63 -0
- package/vendor/gepa-python/src/gepa/utils/code_execution.py +626 -0
- package/vendor/gepa-python/src/gepa/utils/stdio_capture.py +145 -0
- package/vendor/gepa-python/src/gepa/utils/stop_condition.py +230 -0
- package/vendor/gepa-python/src/gepa/visualization.py +359 -0
- package/vendor/orizu-gepa/pyproject.toml +18 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/__init__.py +9 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/__main__.py +5 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/adapter.py +138 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/callbacks.py +666 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/cli.py +7 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/engine.py +269 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/instruction_set_loader.py +52 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/preflight.py +64 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/reflection.py +186 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/runtime.py +721 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/skilled_proposer_bridge.py +381 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/stop_conditions.py +55 -0
- package/vendor/orizu-gepa/src/orizu_gepa_connector/translator.py +203 -0
- package/vendor/orizu-gepa-python/pyproject.toml +1 -1
- package/vendor/orizu-gepa-python/src/orizu_gepa/cli.py +1 -0
- package/vendor/orizu-gepa-python/src/orizu_gepa/client.py +52 -7
- package/vendor/orizu-gepa-python/src/orizu_gepa/local_log.py +52 -0
- package/vendor/orizu-gepa-python/src/orizu_gepa/optimizer.py +10 -5
- package/vendor/orizu-gepa-python/src/orizu_gepa/reflection.py +323 -68
- package/vendor/orizu-gepa-python/src/orizu_gepa/runner.py +207 -13
- package/vendor/skills/orizu-cli/SKILL.md +3 -1
- package/vendor/skills/orizu-cli/references/cli-reference.md +85 -1
- package/vendor/skills/orizu-cli/references/migrate-existing-gepa-setup.md +303 -0
- package/vendor/skills/orizu-cli/references/optimization-reports.md +1 -1
- package/vendor/skills/orizu-cli/references/optimization-with-gepa.md +26 -1
- package/vendor/skills/orizu-cli/references/prompt-control-plane.md +113 -10
|
@@ -0,0 +1,757 @@
|
|
|
1
|
+
import { spawn, spawnSync } from 'node:child_process'
|
|
2
|
+
import { createHash, randomUUID } from 'node:crypto'
|
|
3
|
+
import {
|
|
4
|
+
existsSync,
|
|
5
|
+
mkdirSync,
|
|
6
|
+
readFileSync,
|
|
7
|
+
readdirSync,
|
|
8
|
+
realpathSync,
|
|
9
|
+
renameSync,
|
|
10
|
+
rmSync,
|
|
11
|
+
statSync,
|
|
12
|
+
writeFileSync,
|
|
13
|
+
} from 'node:fs'
|
|
14
|
+
import { delimiter, join } from 'node:path'
|
|
15
|
+
import { devNull } from 'node:os'
|
|
16
|
+
|
|
17
|
+
const PYTHON_IDENTITY_PROGRAM = String.raw`
|
|
18
|
+
import json
|
|
19
|
+
import os
|
|
20
|
+
import platform
|
|
21
|
+
import sys
|
|
22
|
+
import sysconfig
|
|
23
|
+
|
|
24
|
+
print(json.dumps({
|
|
25
|
+
"implementation": platform.python_implementation(),
|
|
26
|
+
"version": list(sys.version_info[:3]),
|
|
27
|
+
"executable": os.path.realpath(sys.executable),
|
|
28
|
+
"platform": sysconfig.get_platform(),
|
|
29
|
+
"cacheTag": sys.implementation.cache_tag,
|
|
30
|
+
"soabi": sysconfig.get_config_var("SOABI"),
|
|
31
|
+
"system": platform.system(),
|
|
32
|
+
"machine": platform.machine(),
|
|
33
|
+
"osVersion": platform.mac_ver()[0] if platform.system() == "Darwin" else platform.release(),
|
|
34
|
+
"libc": list(platform.libc_ver()),
|
|
35
|
+
}))
|
|
36
|
+
`
|
|
37
|
+
|
|
38
|
+
const SSL_CERT_FILE_PROGRAM = String.raw`
|
|
39
|
+
import os
|
|
40
|
+
import ssl
|
|
41
|
+
|
|
42
|
+
paths = ssl.get_default_verify_paths()
|
|
43
|
+
for candidate in (paths.cafile, paths.openssl_cafile):
|
|
44
|
+
if candidate and os.path.isfile(candidate):
|
|
45
|
+
print(os.path.realpath(candidate))
|
|
46
|
+
break
|
|
47
|
+
`
|
|
48
|
+
|
|
49
|
+
const IMPORT_SMOKE_PROGRAM = String.raw`
|
|
50
|
+
import dspy
|
|
51
|
+
import skilled_proposer
|
|
52
|
+
from skilled_proposer import SkilledProposer
|
|
53
|
+
|
|
54
|
+
SkilledProposer(skills=[])
|
|
55
|
+
`
|
|
56
|
+
|
|
57
|
+
const PUBLISH_LOCK_PROGRAM = String.raw`
|
|
58
|
+
import fcntl
|
|
59
|
+
import os
|
|
60
|
+
import signal
|
|
61
|
+
import sys
|
|
62
|
+
import time
|
|
63
|
+
|
|
64
|
+
descriptor = os.open(sys.argv[1], os.O_CREAT | os.O_RDWR, 0o600)
|
|
65
|
+
try:
|
|
66
|
+
try:
|
|
67
|
+
fcntl.flock(descriptor, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
68
|
+
except BlockingIOError:
|
|
69
|
+
print("waiting", flush=True)
|
|
70
|
+
fcntl.flock(descriptor, fcntl.LOCK_EX)
|
|
71
|
+
node_pid = int(sys.argv[2])
|
|
72
|
+
guardian_pid = os.fork()
|
|
73
|
+
if guardian_pid == 0:
|
|
74
|
+
for stream_descriptor in (0, 1, 2):
|
|
75
|
+
try:
|
|
76
|
+
os.close(stream_descriptor)
|
|
77
|
+
except OSError:
|
|
78
|
+
pass
|
|
79
|
+
signal.signal(signal.SIGTERM, lambda _signal, _frame: sys.exit(0))
|
|
80
|
+
deadline = time.monotonic() + (25 * 60)
|
|
81
|
+
while time.monotonic() < deadline:
|
|
82
|
+
try:
|
|
83
|
+
os.kill(node_pid, 0)
|
|
84
|
+
except ProcessLookupError:
|
|
85
|
+
break
|
|
86
|
+
time.sleep(0.1)
|
|
87
|
+
sys.exit(0)
|
|
88
|
+
print(f"guardian:{guardian_pid}", flush=True)
|
|
89
|
+
print("acquired", flush=True)
|
|
90
|
+
sys.stdin.readline()
|
|
91
|
+
try:
|
|
92
|
+
os.kill(guardian_pid, signal.SIGTERM)
|
|
93
|
+
os.waitpid(guardian_pid, 0)
|
|
94
|
+
except ProcessLookupError:
|
|
95
|
+
pass
|
|
96
|
+
finally:
|
|
97
|
+
os.close(descriptor)
|
|
98
|
+
`
|
|
99
|
+
|
|
100
|
+
const MANAGER_SCHEMA_VERSION = 1
|
|
101
|
+
const MANIFEST_NAME = '.orizu-skilled-proposer-venv.json'
|
|
102
|
+
const LOCK_WAIT_MILLISECONDS = 25
|
|
103
|
+
// This exceeds the longest repair path while holding the lock: existing-cache
|
|
104
|
+
// validation (2m) + venv (2m) + install (10m) + staged validation (2m), plus
|
|
105
|
+
// cleanup/publication/release margin.
|
|
106
|
+
const LOCK_TIMEOUT_MILLISECONDS = 20 * 60 * 1000
|
|
107
|
+
const PYTHON_PROBE_TIMEOUT_MILLISECONDS = 15_000
|
|
108
|
+
const VENV_CREATE_TIMEOUT_MILLISECONDS = 2 * 60 * 1000
|
|
109
|
+
const VALIDATION_TIMEOUT_MILLISECONDS = 60_000
|
|
110
|
+
const PIP_INSTALL_TIMEOUT_MILLISECONDS = 10 * 60 * 1000
|
|
111
|
+
const TEST_BARRIER_TIMEOUT_MILLISECONDS = 60_000
|
|
112
|
+
const ABANDONED_STAGE_MAX_AGE_MILLISECONDS = 24 * 60 * 60 * 1000
|
|
113
|
+
const SSL_CERT_FILE_UNRESOLVED_WARNING = {
|
|
114
|
+
code: 'ALI_1505_SSL_CERT_FILE_UNRESOLVED',
|
|
115
|
+
message: 'no system CA bundle was resolved; pip will use its bundled trust store, but provider traffic may require SSL_CERT_FILE',
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export class SkilledProposerVenvError extends Error {
|
|
119
|
+
constructor(code, message, report = {}) {
|
|
120
|
+
super(message)
|
|
121
|
+
this.name = 'SkilledProposerVenvError'
|
|
122
|
+
this.code = code
|
|
123
|
+
this.report = report
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function parseManagerArguments(argv) {
|
|
128
|
+
const options = { json: false, noIndex: false }
|
|
129
|
+
const valueOptions = new Map([
|
|
130
|
+
['--python', 'python'],
|
|
131
|
+
['--cache-root', 'cacheRoot'],
|
|
132
|
+
['--lock', 'lock'],
|
|
133
|
+
['--wheelhouse', 'wheelhouse'],
|
|
134
|
+
['--vendored-gepa-path', 'vendoredGepaPath'],
|
|
135
|
+
])
|
|
136
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
137
|
+
const argument = argv[index]
|
|
138
|
+
if (argument === '--json') {
|
|
139
|
+
options.json = true
|
|
140
|
+
continue
|
|
141
|
+
}
|
|
142
|
+
if (argument === '--no-index') {
|
|
143
|
+
options.noIndex = true
|
|
144
|
+
continue
|
|
145
|
+
}
|
|
146
|
+
const key = valueOptions.get(argument)
|
|
147
|
+
if (!key) {
|
|
148
|
+
throw new SkilledProposerVenvError('ALI_1505_INVALID_ARGUMENT', `unknown argument: ${argument}`)
|
|
149
|
+
}
|
|
150
|
+
const value = argv[index + 1]
|
|
151
|
+
if (!value || value.startsWith('--')) {
|
|
152
|
+
throw new SkilledProposerVenvError('ALI_1505_INVALID_ARGUMENT', `${argument} requires a value`)
|
|
153
|
+
}
|
|
154
|
+
options[key] = value
|
|
155
|
+
index += 1
|
|
156
|
+
}
|
|
157
|
+
for (const key of ['python', 'cacheRoot', 'lock']) {
|
|
158
|
+
if (!options[key]) {
|
|
159
|
+
throw new SkilledProposerVenvError('ALI_1505_INVALID_ARGUMENT', `--${key.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`)} is required`)
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
if (options.noIndex && !options.wheelhouse) {
|
|
163
|
+
throw new SkilledProposerVenvError('ALI_1505_INVALID_ARGUMENT', '--no-index requires --wheelhouse')
|
|
164
|
+
}
|
|
165
|
+
return options
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function parsePythonIdentity(stdout, configuredPython) {
|
|
169
|
+
const trimmed = stdout.trim()
|
|
170
|
+
try {
|
|
171
|
+
const value = JSON.parse(trimmed)
|
|
172
|
+
if (Array.isArray(value.version) && value.version.length >= 2) return value
|
|
173
|
+
} catch {
|
|
174
|
+
// The approved outer red gate uses a deliberately tiny fake executable
|
|
175
|
+
// that prints only a version. This preserves mutation-free preflight.
|
|
176
|
+
}
|
|
177
|
+
const match = /^(\d+)\.(\d+)\.(\d+)$/.exec(trimmed)
|
|
178
|
+
if (match) {
|
|
179
|
+
const version = match.slice(1).map(Number)
|
|
180
|
+
const [major, minor] = version
|
|
181
|
+
if (major !== 3 || minor < 10 || minor >= 15) {
|
|
182
|
+
return {
|
|
183
|
+
implementation: 'CPython',
|
|
184
|
+
version,
|
|
185
|
+
executable: configuredPython,
|
|
186
|
+
platform: 'unsupported-version-probe',
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
throw new SkilledProposerVenvError(
|
|
191
|
+
'ALI_1505_PYTHON_PREFLIGHT_FAILED',
|
|
192
|
+
`unable to characterize selected Python: ${configuredPython}`,
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function versionAtLeast(actual, required) {
|
|
197
|
+
const parts = String(actual).split('.').map(part => Number.parseInt(part, 10) || 0)
|
|
198
|
+
for (let index = 0; index < required.length; index += 1) {
|
|
199
|
+
if ((parts[index] || 0) > required[index]) return true
|
|
200
|
+
if ((parts[index] || 0) < required[index]) return false
|
|
201
|
+
}
|
|
202
|
+
return true
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function assertSupportedWheelTarget(identity) {
|
|
206
|
+
// `platform.libc_ver()` is not merely cache identity on Linux: it is also a
|
|
207
|
+
// conservative admission check for the locked glibc wheel matrix. A bad
|
|
208
|
+
// guess can refuse a compatible host (named, before mutation), but cannot
|
|
209
|
+
// admit an sdist or make pip install an incompatible wheel because the
|
|
210
|
+
// customer install remains hash-locked and binary-only.
|
|
211
|
+
const machine = String(identity.machine).toLowerCase()
|
|
212
|
+
const isSupportedMac = identity.system === 'Darwin'
|
|
213
|
+
&& machine === 'arm64'
|
|
214
|
+
&& versionAtLeast(identity.osVersion, [15])
|
|
215
|
+
const [libcName, libcVersion] = Array.isArray(identity.libc) ? identity.libc : []
|
|
216
|
+
const isSupportedLinux = identity.system === 'Linux'
|
|
217
|
+
&& ['x86_64', 'amd64'].includes(machine)
|
|
218
|
+
&& libcName === 'glibc'
|
|
219
|
+
&& versionAtLeast(libcVersion, [2, 28])
|
|
220
|
+
if (!isSupportedMac && !isSupportedLinux) {
|
|
221
|
+
throw new SkilledProposerVenvError(
|
|
222
|
+
'ALI_1505_NO_LOCKED_BINARY_WHEEL',
|
|
223
|
+
`the skilled-proposer lock has no admitted wheel target for ${identity.system} ${identity.machine} (${identity.platform}, ${libcName || 'unknown-libc'} ${libcVersion || ''})`,
|
|
224
|
+
)
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function spawnPython(python, args, env, timeout = PYTHON_PROBE_TIMEOUT_MILLISECONDS) {
|
|
229
|
+
return spawnSync(python, args, { encoding: 'utf8', env, timeout })
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function isolatedPythonProbeEnv() {
|
|
233
|
+
const env = { ...process.env, PYTHONNOUSERSITE: '1' }
|
|
234
|
+
for (const name of Object.keys(env)) {
|
|
235
|
+
if (name.startsWith('PIP_')) delete env[name]
|
|
236
|
+
}
|
|
237
|
+
for (const name of [
|
|
238
|
+
'PYTHONHOME',
|
|
239
|
+
'PYTHONPATH',
|
|
240
|
+
'PYTHONUSERBASE',
|
|
241
|
+
'REQUESTS_CA_BUNDLE',
|
|
242
|
+
'CURL_CA_BUNDLE',
|
|
243
|
+
]) delete env[name]
|
|
244
|
+
env.PIP_CONFIG_FILE = devNull
|
|
245
|
+
env.PIP_DISABLE_PIP_VERSION_CHECK = '1'
|
|
246
|
+
env.PIP_NO_INPUT = '1'
|
|
247
|
+
return env
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
export function characterizePython(python, probeEnv = isolatedPythonProbeEnv()) {
|
|
251
|
+
const result = spawnPython(python, ['-c', PYTHON_IDENTITY_PROGRAM], probeEnv)
|
|
252
|
+
if (result.error || result.status !== 0) {
|
|
253
|
+
const detail = result.error?.message || result.stderr || result.stdout || 'no subprocess output'
|
|
254
|
+
throw new SkilledProposerVenvError(
|
|
255
|
+
'ALI_1505_PYTHON_PREFLIGHT_FAILED',
|
|
256
|
+
`selected Python preflight failed: ${detail.trim()}`,
|
|
257
|
+
)
|
|
258
|
+
}
|
|
259
|
+
const identity = parsePythonIdentity(result.stdout, python)
|
|
260
|
+
const [major, minor] = identity.version
|
|
261
|
+
if (String(identity.implementation).toLowerCase() !== 'cpython'
|
|
262
|
+
|| major !== 3 || minor < 10 || minor >= 15) {
|
|
263
|
+
throw new SkilledProposerVenvError(
|
|
264
|
+
'ALI_1505_UNSUPPORTED_CPYTHON',
|
|
265
|
+
`skilled-proposer requires CPython >=3.10,<3.15; received ${identity.implementation} ${identity.version.join('.')}`,
|
|
266
|
+
)
|
|
267
|
+
}
|
|
268
|
+
assertSupportedWheelTarget(identity)
|
|
269
|
+
return {
|
|
270
|
+
...identity,
|
|
271
|
+
executable: realpathSync(identity.executable),
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
function readAndValidateHashLock(lockPath) {
|
|
276
|
+
let contents
|
|
277
|
+
try {
|
|
278
|
+
contents = readFileSync(lockPath, 'utf8')
|
|
279
|
+
} catch (error) {
|
|
280
|
+
if (error?.code === 'ENOENT') {
|
|
281
|
+
throw new SkilledProposerVenvError(
|
|
282
|
+
'ALI_1505_LOCK_MISSING',
|
|
283
|
+
`skilled-proposer lock file does not exist: ${lockPath}`,
|
|
284
|
+
)
|
|
285
|
+
}
|
|
286
|
+
throw error
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
const requirements = []
|
|
290
|
+
let pending = ''
|
|
291
|
+
for (const line of contents.split(/\r?\n/)) {
|
|
292
|
+
const trimmed = line.trim()
|
|
293
|
+
if (!trimmed || trimmed.startsWith('#')) continue
|
|
294
|
+
pending += `${pending ? ' ' : ''}${trimmed.replace(/\\$/, '').trim()}`
|
|
295
|
+
if (!trimmed.endsWith('\\')) {
|
|
296
|
+
requirements.push(pending)
|
|
297
|
+
pending = ''
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
if (pending) requirements.push(pending)
|
|
301
|
+
if (requirements.length === 0) {
|
|
302
|
+
throw new SkilledProposerVenvError(
|
|
303
|
+
'ALI_1505_LOCK_UNHASHED_REQUIREMENT',
|
|
304
|
+
`skilled-proposer lock file contains no requirements: ${lockPath}`,
|
|
305
|
+
)
|
|
306
|
+
}
|
|
307
|
+
for (const requirement of requirements) {
|
|
308
|
+
const isExactPin = /^[A-Za-z0-9_.-]+(?:\[[^\]]+\])?==[^\s;]+/.test(requirement)
|
|
309
|
+
const hashes = [...requirement.matchAll(/(?:^|\s)--hash=([^\s]+)/g)].map(match => match[1])
|
|
310
|
+
const hashTokens = [...requirement.matchAll(/(?:^|\s)(--hash(?:=[^\s]*)?)(?=\s|$)/g)]
|
|
311
|
+
.map(match => match[1])
|
|
312
|
+
const hasMalformedHash = hashes.some(hash => !/^sha256:[a-fA-F0-9]{64}$/.test(hash))
|
|
313
|
+
|| hashTokens.length !== hashes.length
|
|
314
|
+
if (hasMalformedHash) {
|
|
315
|
+
throw new SkilledProposerVenvError(
|
|
316
|
+
'ALI_1505_LOCK_FORMAT_INVALID',
|
|
317
|
+
`dependency contains a malformed sha256 hash: ${requirement}`,
|
|
318
|
+
)
|
|
319
|
+
}
|
|
320
|
+
if (!isExactPin || hashes.length === 0) {
|
|
321
|
+
throw new SkilledProposerVenvError(
|
|
322
|
+
'ALI_1505_LOCK_UNHASHED_REQUIREMENT',
|
|
323
|
+
`every dependency must be an exact pin with a sha256 hash: ${requirement}`,
|
|
324
|
+
)
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return contents
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function resolveSslCertFile(pythonIdentity, probeEnv) {
|
|
331
|
+
if (process.env.SSL_CERT_FILE) return process.env.SSL_CERT_FILE
|
|
332
|
+
if (testHook('ORIZU_ALI1505_TEST_FORCE_CA_UNRESOLVED') === '1') return null
|
|
333
|
+
const result = spawnPython(pythonIdentity.executable, ['-c', SSL_CERT_FILE_PROGRAM], probeEnv)
|
|
334
|
+
const candidate = result.status === 0 ? result.stdout.trim() : ''
|
|
335
|
+
if (candidate && existsSync(candidate)) return candidate
|
|
336
|
+
for (const commonPath of [
|
|
337
|
+
'/etc/ssl/cert.pem',
|
|
338
|
+
'/etc/ssl/certs/ca-certificates.crt',
|
|
339
|
+
'/etc/pki/tls/certs/ca-bundle.crt',
|
|
340
|
+
]) {
|
|
341
|
+
if (existsSync(commonPath)) return commonPath
|
|
342
|
+
}
|
|
343
|
+
return null
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
function buildPublishKey(pythonIdentity, lockContents) {
|
|
347
|
+
const lockDigest = createHash('sha256').update(lockContents).digest('hex')
|
|
348
|
+
const identity = {
|
|
349
|
+
managerSchemaVersion: MANAGER_SCHEMA_VERSION,
|
|
350
|
+
implementation: pythonIdentity.implementation,
|
|
351
|
+
version: pythonIdentity.version,
|
|
352
|
+
executable: pythonIdentity.executable,
|
|
353
|
+
platform: pythonIdentity.platform,
|
|
354
|
+
cacheTag: pythonIdentity.cacheTag,
|
|
355
|
+
soabi: pythonIdentity.soabi,
|
|
356
|
+
system: pythonIdentity.system,
|
|
357
|
+
machine: pythonIdentity.machine,
|
|
358
|
+
osVersion: pythonIdentity.osVersion,
|
|
359
|
+
libc: pythonIdentity.libc,
|
|
360
|
+
lockDigest,
|
|
361
|
+
}
|
|
362
|
+
return {
|
|
363
|
+
lockDigest,
|
|
364
|
+
publishKey: createHash('sha256').update(JSON.stringify(identity)).digest('hex'),
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
function venvPython(venv) {
|
|
369
|
+
return process.platform === 'win32' ? join(venv, 'Scripts', 'python.exe') : join(venv, 'bin', 'python')
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function runChecked(
|
|
373
|
+
python,
|
|
374
|
+
args,
|
|
375
|
+
env,
|
|
376
|
+
code,
|
|
377
|
+
description,
|
|
378
|
+
report = {},
|
|
379
|
+
timeout = VALIDATION_TIMEOUT_MILLISECONDS,
|
|
380
|
+
) {
|
|
381
|
+
const result = spawnPython(python, args, env, timeout)
|
|
382
|
+
if (result.error || result.status !== 0) {
|
|
383
|
+
const detail = result.error?.message || result.stderr || result.stdout || 'no subprocess output'
|
|
384
|
+
throw new SkilledProposerVenvError(code, `${description}: ${detail.trim()}`, report)
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function readValidManifest(venv, publishKey, lockDigest) {
|
|
389
|
+
try {
|
|
390
|
+
const manifest = JSON.parse(readFileSync(join(venv, MANIFEST_NAME), 'utf8'))
|
|
391
|
+
if (manifest.managerSchemaVersion !== MANAGER_SCHEMA_VERSION) return null
|
|
392
|
+
if (manifest.publishKey !== publishKey || manifest.lockDigest !== lockDigest) return null
|
|
393
|
+
if (!existsSync(join(venv, 'pyvenv.cfg'))) return null
|
|
394
|
+
if (!existsSync(venvPython(venv))) return null
|
|
395
|
+
return manifest
|
|
396
|
+
} catch {
|
|
397
|
+
return null
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
function smokeEnvironment(childEnv, pythonPathEntries) {
|
|
402
|
+
return {
|
|
403
|
+
...childEnv,
|
|
404
|
+
PYTHONPATH: pythonPathEntries.join(delimiter),
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function validatePublishedVenv(venv, manifest, childEnv, pythonPathEntries) {
|
|
409
|
+
if (!manifest) return null
|
|
410
|
+
try {
|
|
411
|
+
const python = venvPython(venv)
|
|
412
|
+
runChecked(
|
|
413
|
+
python,
|
|
414
|
+
['-m', 'pip', 'check'],
|
|
415
|
+
childEnv,
|
|
416
|
+
'ALI_1505_PIP_CHECK_FAILED',
|
|
417
|
+
'published venv failed pip check',
|
|
418
|
+
)
|
|
419
|
+
runChecked(
|
|
420
|
+
python,
|
|
421
|
+
['-c', IMPORT_SMOKE_PROGRAM],
|
|
422
|
+
smokeEnvironment(childEnv, pythonPathEntries),
|
|
423
|
+
'ALI_1505_IMPORT_SMOKE_FAILED',
|
|
424
|
+
'published venv failed import smoke test',
|
|
425
|
+
)
|
|
426
|
+
return manifest
|
|
427
|
+
} catch {
|
|
428
|
+
return null
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
function wait(milliseconds) {
|
|
433
|
+
return new Promise(resolve => setTimeout(resolve, milliseconds))
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function testHook(name) {
|
|
437
|
+
if (process.env.ORIZU_ALI1505_TEST_HOOKS !== '1') return undefined
|
|
438
|
+
return process.env[name]
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
async function waitAtTestBarrier(cacheRoot) {
|
|
442
|
+
const barrier = testHook('ORIZU_ALI1505_TEST_START_BARRIER')
|
|
443
|
+
if (!barrier) return
|
|
444
|
+
const readyDirectory = join(cacheRoot, 'test-start-barrier-ready')
|
|
445
|
+
mkdirSync(readyDirectory, { recursive: true })
|
|
446
|
+
writeFileSync(join(readyDirectory, `${process.pid}-${randomUUID()}`), '')
|
|
447
|
+
const startedAt = Date.now()
|
|
448
|
+
while (existsSync(barrier)) {
|
|
449
|
+
if (Date.now() - startedAt >= TEST_BARRIER_TIMEOUT_MILLISECONDS) {
|
|
450
|
+
throw new SkilledProposerVenvError(
|
|
451
|
+
'ALI_1505_TEST_START_BARRIER_TIMEOUT',
|
|
452
|
+
`timed out waiting for ALI-1505 test barrier: ${barrier}`,
|
|
453
|
+
)
|
|
454
|
+
}
|
|
455
|
+
await wait(LOCK_WAIT_MILLISECONDS)
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
function acquirePublishLock(python, lockFile, env) {
|
|
460
|
+
return new Promise((resolve, reject) => {
|
|
461
|
+
const child = spawn(python, ['-u', '-c', PUBLISH_LOCK_PROGRAM, lockFile, String(process.pid)], {
|
|
462
|
+
env,
|
|
463
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
464
|
+
})
|
|
465
|
+
let stdout = ''
|
|
466
|
+
let stderr = ''
|
|
467
|
+
let waitedForPublishLock = false
|
|
468
|
+
let acquired = false
|
|
469
|
+
let guardianPid = null
|
|
470
|
+
const timeout = setTimeout(() => {
|
|
471
|
+
child.kill()
|
|
472
|
+
reject(new SkilledProposerVenvError(
|
|
473
|
+
'ALI_1505_PUBLISH_LOCK_TIMEOUT',
|
|
474
|
+
`timed out waiting for publish lock: ${lockFile}`,
|
|
475
|
+
{ waitedForPublishLock: true },
|
|
476
|
+
))
|
|
477
|
+
}, LOCK_TIMEOUT_MILLISECONDS)
|
|
478
|
+
|
|
479
|
+
child.stdout.setEncoding('utf8')
|
|
480
|
+
child.stderr.setEncoding('utf8')
|
|
481
|
+
child.stderr.on('data', chunk => { stderr += chunk })
|
|
482
|
+
child.stdout.on('data', chunk => {
|
|
483
|
+
stdout += chunk
|
|
484
|
+
const lines = stdout.split(/\r?\n/)
|
|
485
|
+
stdout = lines.pop() || ''
|
|
486
|
+
for (const line of lines) {
|
|
487
|
+
if (line === 'waiting') waitedForPublishLock = true
|
|
488
|
+
if (line.startsWith('guardian:')) guardianPid = Number.parseInt(line.slice('guardian:'.length), 10)
|
|
489
|
+
if (line === 'acquired' && !acquired) {
|
|
490
|
+
acquired = true
|
|
491
|
+
clearTimeout(timeout)
|
|
492
|
+
if (!Number.isSafeInteger(guardianPid) || guardianPid <= 0) {
|
|
493
|
+
child.kill()
|
|
494
|
+
reject(new SkilledProposerVenvError(
|
|
495
|
+
'ALI_1505_PUBLISH_LOCK_FAILED',
|
|
496
|
+
'publish-lock holder did not report a guardian process',
|
|
497
|
+
{ waitedForPublishLock },
|
|
498
|
+
))
|
|
499
|
+
return
|
|
500
|
+
}
|
|
501
|
+
resolve({ child, guardianPid, waitedForPublishLock })
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
})
|
|
505
|
+
child.once('error', error => {
|
|
506
|
+
if (acquired) return
|
|
507
|
+
clearTimeout(timeout)
|
|
508
|
+
reject(new SkilledProposerVenvError(
|
|
509
|
+
'ALI_1505_PUBLISH_LOCK_FAILED',
|
|
510
|
+
`failed to start publish-lock holder: ${error.message}`,
|
|
511
|
+
{ waitedForPublishLock },
|
|
512
|
+
))
|
|
513
|
+
})
|
|
514
|
+
child.once('exit', (code, signal) => {
|
|
515
|
+
if (acquired) return
|
|
516
|
+
clearTimeout(timeout)
|
|
517
|
+
reject(new SkilledProposerVenvError(
|
|
518
|
+
'ALI_1505_PUBLISH_LOCK_FAILED',
|
|
519
|
+
`publish-lock holder exited before acquisition (${signal || code}): ${stderr.trim() || stdout.trim() || 'no subprocess output'}`,
|
|
520
|
+
{ waitedForPublishLock },
|
|
521
|
+
))
|
|
522
|
+
})
|
|
523
|
+
})
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
async function releasePublishLock(lock) {
|
|
527
|
+
if (!lock?.child) return
|
|
528
|
+
if (lock.child.exitCode !== null || lock.child.signalCode !== null) {
|
|
529
|
+
try {
|
|
530
|
+
process.kill(lock.guardianPid, 'SIGTERM')
|
|
531
|
+
} catch (error) {
|
|
532
|
+
if (error?.code !== 'ESRCH') throw error
|
|
533
|
+
}
|
|
534
|
+
return
|
|
535
|
+
}
|
|
536
|
+
const exited = new Promise(resolve => lock.child.once('exit', resolve))
|
|
537
|
+
lock.child.stdin.end('release\n')
|
|
538
|
+
const timeout = setTimeout(() => lock.child.kill(), PYTHON_PROBE_TIMEOUT_MILLISECONDS)
|
|
539
|
+
await exited
|
|
540
|
+
clearTimeout(timeout)
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
async function killPublishLockLeaderAtTestHook(lock) {
|
|
544
|
+
if (testHook('ORIZU_ALI1505_TEST_KILL_PUBLISH_LOCK_LEADER') !== 'after-acquire') return
|
|
545
|
+
const exited = new Promise(resolve => lock.child.once('exit', resolve))
|
|
546
|
+
lock.child.kill('SIGKILL')
|
|
547
|
+
await exited
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
function removeAbandonedStages(venvsRoot, publishKey) {
|
|
551
|
+
const stagePrefix = `.${publishKey}-`
|
|
552
|
+
for (const entry of readdirSync(venvsRoot)) {
|
|
553
|
+
const stagePath = join(venvsRoot, entry)
|
|
554
|
+
const isCurrentKeyStage = entry.startsWith(stagePrefix) && entry.endsWith('.tmp')
|
|
555
|
+
const isManagedStage = /^\.[a-f0-9]{64}-.+\.tmp$/.test(entry)
|
|
556
|
+
let isAgedOtherKeyStage = false
|
|
557
|
+
if (isManagedStage) {
|
|
558
|
+
try {
|
|
559
|
+
isAgedOtherKeyStage = Date.now() - statSync(stagePath).mtimeMs
|
|
560
|
+
>= ABANDONED_STAGE_MAX_AGE_MILLISECONDS
|
|
561
|
+
} catch (error) {
|
|
562
|
+
if (error?.code === 'ENOENT') continue
|
|
563
|
+
throw error
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
// The current publish lock makes every same-key stage abandoned. Across
|
|
567
|
+
// keys, the 24-hour floor is well above the 25-minute guardian lifetime,
|
|
568
|
+
// so an admitted live builder is never collected without its own lock.
|
|
569
|
+
if (isCurrentKeyStage || isAgedOtherKeyStage) {
|
|
570
|
+
rmSync(stagePath, { recursive: true, force: true })
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
function installArguments(options, stagePython) {
|
|
576
|
+
const args = [
|
|
577
|
+
stagePython,
|
|
578
|
+
'-m', 'pip', 'install',
|
|
579
|
+
'--require-hashes',
|
|
580
|
+
'--only-binary=:all:',
|
|
581
|
+
]
|
|
582
|
+
if (options.noIndex) args.push('--no-index', '--find-links', options.wheelhouse)
|
|
583
|
+
args.push('--requirement', options.lock)
|
|
584
|
+
return args
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
function installFailureCode(result) {
|
|
588
|
+
const output = `${result.stdout || ''}\n${result.stderr || ''}`
|
|
589
|
+
if (/do not match the hashes|hash mismatch/i.test(output)) {
|
|
590
|
+
return 'ALI_1505_ARTIFACT_HASH_MISMATCH'
|
|
591
|
+
}
|
|
592
|
+
if (/hashes are required/i.test(output)) return 'ALI_1505_LOCK_UNHASHED_REQUIREMENT'
|
|
593
|
+
if (/no matching distribution|could not find a version|not a supported wheel/i.test(output)) {
|
|
594
|
+
return 'ALI_1505_NO_LOCKED_BINARY_WHEEL'
|
|
595
|
+
}
|
|
596
|
+
return 'ALI_1505_PIP_INSTALL_FAILED'
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
function isolatedChildEnv(sslCertFile) {
|
|
600
|
+
const env = { ...isolatedPythonProbeEnv() }
|
|
601
|
+
if (sslCertFile) env.SSL_CERT_FILE = sslCertFile
|
|
602
|
+
else delete env.SSL_CERT_FILE
|
|
603
|
+
return env
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
export async function ensureSkilledProposerVenv(options) {
|
|
607
|
+
// This must stay first: unsupported interpreters may not create cache state or
|
|
608
|
+
// invoke a mutating `python -m` command.
|
|
609
|
+
const probeEnv = isolatedPythonProbeEnv()
|
|
610
|
+
const pythonIdentity = characterizePython(options.python, probeEnv)
|
|
611
|
+
const lockContents = readAndValidateHashLock(options.lock)
|
|
612
|
+
const sslCertFile = resolveSslCertFile(pythonIdentity, probeEnv)
|
|
613
|
+
const childEnv = isolatedChildEnv(sslCertFile)
|
|
614
|
+
const { lockDigest, publishKey } = buildPublishKey(pythonIdentity, lockContents)
|
|
615
|
+
const finalVenv = join(options.cacheRoot, 'venvs', publishKey)
|
|
616
|
+
const lockFile = join(options.cacheRoot, 'publish-locks', `${publishKey}.lock`)
|
|
617
|
+
const pythonPathEntries = options.vendoredGepaPath ? [options.vendoredGepaPath] : []
|
|
618
|
+
const baseReport = {
|
|
619
|
+
venv: finalVenv,
|
|
620
|
+
publishKey,
|
|
621
|
+
publishedVenv: false,
|
|
622
|
+
waitedForPublishLock: false,
|
|
623
|
+
pythonPathEntries,
|
|
624
|
+
executedPipArgv: null,
|
|
625
|
+
sslCertFile,
|
|
626
|
+
warnings: sslCertFile ? [] : [SSL_CERT_FILE_UNRESOLVED_WARNING],
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
mkdirSync(join(options.cacheRoot, 'venvs'), { recursive: true })
|
|
630
|
+
mkdirSync(join(options.cacheRoot, 'publish-locks'), { recursive: true })
|
|
631
|
+
await waitAtTestBarrier(options.cacheRoot)
|
|
632
|
+
|
|
633
|
+
const existing = validatePublishedVenv(
|
|
634
|
+
finalVenv,
|
|
635
|
+
readValidManifest(finalVenv, publishKey, lockDigest),
|
|
636
|
+
childEnv,
|
|
637
|
+
pythonPathEntries,
|
|
638
|
+
)
|
|
639
|
+
if (existing) return baseReport
|
|
640
|
+
|
|
641
|
+
const lock = await acquirePublishLock(pythonIdentity.executable, lockFile, childEnv)
|
|
642
|
+
await killPublishLockLeaderAtTestHook(lock)
|
|
643
|
+
const lockedReport = { ...baseReport, waitedForPublishLock: lock.waitedForPublishLock }
|
|
644
|
+
|
|
645
|
+
const stage = join(options.cacheRoot, 'venvs', `.${publishKey}-${process.pid}-${randomUUID()}.tmp`)
|
|
646
|
+
try {
|
|
647
|
+
// Holding the per-key publish lock proves that no live builder for this key
|
|
648
|
+
// can own one of these sibling staging directories.
|
|
649
|
+
removeAbandonedStages(join(options.cacheRoot, 'venvs'), publishKey)
|
|
650
|
+
const afterLockExisting = validatePublishedVenv(
|
|
651
|
+
finalVenv,
|
|
652
|
+
readValidManifest(finalVenv, publishKey, lockDigest),
|
|
653
|
+
childEnv,
|
|
654
|
+
pythonPathEntries,
|
|
655
|
+
)
|
|
656
|
+
if (afterLockExisting) {
|
|
657
|
+
return lockedReport
|
|
658
|
+
}
|
|
659
|
+
if (existsSync(finalVenv)) rmSync(finalVenv, { recursive: true, force: true })
|
|
660
|
+
|
|
661
|
+
runChecked(
|
|
662
|
+
pythonIdentity.executable,
|
|
663
|
+
['-m', 'venv', stage],
|
|
664
|
+
childEnv,
|
|
665
|
+
'ALI_1505_VENV_CREATE_FAILED',
|
|
666
|
+
'failed to create staged venv',
|
|
667
|
+
lockedReport,
|
|
668
|
+
VENV_CREATE_TIMEOUT_MILLISECONDS,
|
|
669
|
+
)
|
|
670
|
+
const stagePython = venvPython(stage)
|
|
671
|
+
const executedPipArgv = installArguments(options, stagePython)
|
|
672
|
+
const installingReport = { ...lockedReport, executedPipArgv }
|
|
673
|
+
const installResult = spawnPython(
|
|
674
|
+
stagePython,
|
|
675
|
+
executedPipArgv.slice(1),
|
|
676
|
+
childEnv,
|
|
677
|
+
PIP_INSTALL_TIMEOUT_MILLISECONDS,
|
|
678
|
+
)
|
|
679
|
+
if (installResult.error || installResult.status !== 0) {
|
|
680
|
+
const detail = installResult.error?.message || installResult.stderr || installResult.stdout || 'no subprocess output'
|
|
681
|
+
throw new SkilledProposerVenvError(
|
|
682
|
+
installFailureCode(installResult),
|
|
683
|
+
`failed to install locked dependencies: ${detail.trim()}`,
|
|
684
|
+
installingReport,
|
|
685
|
+
)
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
if (testHook('ORIZU_ALI1505_TEST_INTERRUPT_AFTER') === 'staged-install') {
|
|
689
|
+
throw new SkilledProposerVenvError(
|
|
690
|
+
'ALI_1505_TEST_INTERRUPT_AFTER_STAGED_INSTALL',
|
|
691
|
+
'interrupted after staged install by ALI-1505 test hook',
|
|
692
|
+
{ ...installingReport, publishedVenv: null },
|
|
693
|
+
)
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
runChecked(
|
|
697
|
+
stagePython,
|
|
698
|
+
['-m', 'pip', 'check'],
|
|
699
|
+
childEnv,
|
|
700
|
+
'ALI_1505_PIP_CHECK_FAILED',
|
|
701
|
+
'staged venv failed pip check',
|
|
702
|
+
installingReport,
|
|
703
|
+
)
|
|
704
|
+
runChecked(
|
|
705
|
+
stagePython,
|
|
706
|
+
['-c', IMPORT_SMOKE_PROGRAM],
|
|
707
|
+
smokeEnvironment(childEnv, pythonPathEntries),
|
|
708
|
+
'ALI_1505_IMPORT_SMOKE_FAILED',
|
|
709
|
+
'staged venv failed import smoke test',
|
|
710
|
+
installingReport,
|
|
711
|
+
)
|
|
712
|
+
writeFileSync(join(stage, MANIFEST_NAME), `${JSON.stringify({
|
|
713
|
+
managerSchemaVersion: MANAGER_SCHEMA_VERSION,
|
|
714
|
+
publishKey,
|
|
715
|
+
lockDigest,
|
|
716
|
+
pythonIdentity,
|
|
717
|
+
executedPipArgv,
|
|
718
|
+
}, null, 2)}\n`)
|
|
719
|
+
try {
|
|
720
|
+
renameSync(stage, finalVenv)
|
|
721
|
+
} catch (error) {
|
|
722
|
+
throw new SkilledProposerVenvError(
|
|
723
|
+
'ALI_1505_VENV_PUBLISH_FAILED',
|
|
724
|
+
`failed to publish the validated venv atomically: ${error instanceof Error ? error.message : String(error)}`,
|
|
725
|
+
installingReport,
|
|
726
|
+
)
|
|
727
|
+
}
|
|
728
|
+
return { ...installingReport, publishedVenv: true }
|
|
729
|
+
} finally {
|
|
730
|
+
rmSync(stage, { recursive: true, force: true })
|
|
731
|
+
await releasePublishLock(lock)
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
export async function runManagerCli(argv) {
|
|
736
|
+
let options = { json: argv.includes('--json') }
|
|
737
|
+
try {
|
|
738
|
+
options = parseManagerArguments(argv)
|
|
739
|
+
const report = await ensureSkilledProposerVenv(options)
|
|
740
|
+
process.stdout.write(`${JSON.stringify(report)}\n`)
|
|
741
|
+
return 0
|
|
742
|
+
} catch (error) {
|
|
743
|
+
const managedError = error instanceof SkilledProposerVenvError
|
|
744
|
+
? error
|
|
745
|
+
: new SkilledProposerVenvError('ALI_1505_VENV_MANAGER_FAILED', String(error))
|
|
746
|
+
if (options.json) {
|
|
747
|
+
process.stdout.write(`${JSON.stringify({
|
|
748
|
+
error: managedError.code,
|
|
749
|
+
message: managedError.message,
|
|
750
|
+
...managedError.report,
|
|
751
|
+
publishedVenv: null,
|
|
752
|
+
})}\n`)
|
|
753
|
+
}
|
|
754
|
+
process.stderr.write(`${managedError.code}: ${managedError.message}\n`)
|
|
755
|
+
return 1
|
|
756
|
+
}
|
|
757
|
+
}
|