Spaces:
Sleeping
Sleeping
Upload folder using huggingface_hub
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .gitattributes +7 -0
- CLAUDE.md +549 -0
- Dockerfile +82 -0
- LICENSE +21 -0
- README.md +99 -4
- __init__.py +11 -0
- advanced_readme.md +210 -0
- client.py +68 -0
- environment/__init__.py +5 -0
- environment/generator.py +382 -0
- environment/grader.py +143 -0
- environment/packages.py +23 -0
- environment/parser.py +51 -0
- environment/stage_runner.py +414 -0
- inference.py +339 -0
- models.py +31 -0
- openenv.yaml +61 -0
- openenv_CI_CD_Doctor.egg-info/PKG-INFO +11 -0
- openenv_CI_CD_Doctor.egg-info/SOURCES.txt +22 -0
- openenv_CI_CD_Doctor.egg-info/dependency_links.txt +1 -0
- openenv_CI_CD_Doctor.egg-info/entry_points.txt +2 -0
- openenv_CI_CD_Doctor.egg-info/requires.txt +5 -0
- openenv_CI_CD_Doctor.egg-info/top_level.txt +1 -0
- pyproject.toml +45 -0
- requirements.txt +6 -0
- server/__init__.py +5 -0
- server/app.py +95 -0
- server/app_2.py +101 -0
- server/environment.py +199 -0
- uv.lock +0 -0
- venv/bin/Activate.ps1 +247 -0
- venv/bin/activate +71 -0
- venv/bin/activate.csh +27 -0
- venv/bin/activate.fish +69 -0
- venv/bin/pip +8 -0
- venv/bin/pip3 +8 -0
- venv/bin/pip3.12 +8 -0
- venv/bin/python +3 -0
- venv/bin/python3 +3 -0
- venv/bin/python3.12 +3 -0
- venv/lib/python3.12/site-packages/pip-24.3.1.dist-info/AUTHORS.txt +799 -0
- venv/lib/python3.12/site-packages/pip-24.3.1.dist-info/INSTALLER +1 -0
- venv/lib/python3.12/site-packages/pip-24.3.1.dist-info/LICENSE.txt +20 -0
- venv/lib/python3.12/site-packages/pip-24.3.1.dist-info/METADATA +90 -0
- venv/lib/python3.12/site-packages/pip-24.3.1.dist-info/RECORD +853 -0
- venv/lib/python3.12/site-packages/pip-24.3.1.dist-info/REQUESTED +0 -0
- venv/lib/python3.12/site-packages/pip-24.3.1.dist-info/WHEEL +5 -0
- venv/lib/python3.12/site-packages/pip-24.3.1.dist-info/entry_points.txt +3 -0
- venv/lib/python3.12/site-packages/pip-24.3.1.dist-info/top_level.txt +1 -0
- venv/lib/python3.12/site-packages/pip/__init__.py +13 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,10 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
venv/bin/python filter=lfs diff=lfs merge=lfs -text
|
| 37 |
+
venv/bin/python3 filter=lfs diff=lfs merge=lfs -text
|
| 38 |
+
venv/bin/python3.12 filter=lfs diff=lfs merge=lfs -text
|
| 39 |
+
venv/lib/python3.12/site-packages/pip/_vendor/distlib/t64-arm.exe filter=lfs diff=lfs merge=lfs -text
|
| 40 |
+
venv/lib/python3.12/site-packages/pip/_vendor/distlib/t64.exe filter=lfs diff=lfs merge=lfs -text
|
| 41 |
+
venv/lib/python3.12/site-packages/pip/_vendor/distlib/w64-arm.exe filter=lfs diff=lfs merge=lfs -text
|
| 42 |
+
venv/lib/python3.12/site-packages/pip/_vendor/distlib/w64.exe filter=lfs diff=lfs merge=lfs -text
|
CLAUDE.md
ADDED
|
@@ -0,0 +1,549 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# CLAUDE.md — CI/CD Doctor (Pipeline Debugging RL Environment)
|
| 2 |
+
|
| 3 |
+
## What This Project Is
|
| 4 |
+
|
| 5 |
+
An **OpenEnv reinforcement learning environment** for an AI competition.
|
| 6 |
+
The agent plays the role of a DevOps engineer debugging a broken CI/CD pipeline.
|
| 7 |
+
It issues free-form shell-like commands, reads logs, edits files, and re-runs
|
| 8 |
+
pipeline stages until the pipeline passes.
|
| 9 |
+
|
| 10 |
+
This is NOT a game. It simulates real DevOps work.
|
| 11 |
+
|
| 12 |
+
---
|
| 13 |
+
|
| 14 |
+
## Current Goal: Complete Phase 1
|
| 15 |
+
|
| 16 |
+
**Phase 1 = Core environment, easy task only, no server yet.**
|
| 17 |
+
|
| 18 |
+
Deliverable: a working Python module where you can call `reset()`, then `step()`,
|
| 19 |
+
then `state()` in a loop and the environment responds correctly.
|
| 20 |
+
|
| 21 |
+
---
|
| 22 |
+
|
| 23 |
+
## Project Structure (target layout)
|
| 24 |
+
|
| 25 |
+
```
|
| 26 |
+
cicd-doctor/
|
| 27 |
+
├── cicd_doctor/
|
| 28 |
+
│ ├── __init__.py
|
| 29 |
+
│ ├── models.py ← Pydantic data models
|
| 30 |
+
│ ├── generator.py ← procedural scenario generator
|
| 31 |
+
│ ├── stage_runner.py ← simulated pipeline stages
|
| 32 |
+
│ ├── parser.py ← free-form command parser
|
| 33 |
+
│ └── environment.py ← core RL environment class
|
| 34 |
+
└── tests/
|
| 35 |
+
└── test_phase1.py ← smoke test for the loop
|
| 36 |
+
```
|
| 37 |
+
|
| 38 |
+
---
|
| 39 |
+
|
| 40 |
+
## Architecture Mental Model
|
| 41 |
+
|
| 42 |
+
```
|
| 43 |
+
Agent issues a command string
|
| 44 |
+
│
|
| 45 |
+
▼
|
| 46 |
+
parser.py ← parses "cat requirements.txt" → structured ParsedCommand
|
| 47 |
+
│
|
| 48 |
+
▼
|
| 49 |
+
environment.py ← looks up command type, mutates in-memory filesystem
|
| 50 |
+
│
|
| 51 |
+
├──► stage_runner.py ← called on "pipeline run" or "pipeline logs"
|
| 52 |
+
│ returns fake-but-realistic logs + exit_code
|
| 53 |
+
│
|
| 54 |
+
└──► returns PipelineObservation to caller
|
| 55 |
+
```
|
| 56 |
+
|
| 57 |
+
The "filesystem" is just a Python dict in memory:
|
| 58 |
+
```python
|
| 59 |
+
filesystem = {
|
| 60 |
+
"requirements.txt": "flask\nnumpy\n",
|
| 61 |
+
"pipeline.yaml": "stages:\n - install\n",
|
| 62 |
+
"logs/install.log": ""
|
| 63 |
+
}
|
| 64 |
+
```
|
| 65 |
+
No real files. No real pip. No real Docker. Pure deterministic simulation.
|
| 66 |
+
|
| 67 |
+
---
|
| 68 |
+
|
| 69 |
+
## File-by-File Specification
|
| 70 |
+
|
| 71 |
+
### `models.py`
|
| 72 |
+
|
| 73 |
+
Three Pydantic models. Keep them minimal for Phase 1.
|
| 74 |
+
|
| 75 |
+
```python
|
| 76 |
+
from pydantic import BaseModel
|
| 77 |
+
from typing import Optional, Dict, Any
|
| 78 |
+
|
| 79 |
+
class PipelineAction(BaseModel):
|
| 80 |
+
command: str # raw string the agent types, e.g. "cat requirements.txt"
|
| 81 |
+
|
| 82 |
+
class PipelineObservation(BaseModel):
|
| 83 |
+
stdout: str # what the agent sees (file contents, logs, error msg)
|
| 84 |
+
exit_code: int # 0 = success, 1 = error
|
| 85 |
+
pipeline_status: str # "not_run" | "running" | "passed" | "failed"
|
| 86 |
+
steps_remaining: int
|
| 87 |
+
done: bool = False
|
| 88 |
+
reward: float = 0.0
|
| 89 |
+
|
| 90 |
+
class PipelineState(BaseModel):
|
| 91 |
+
episode_id: str
|
| 92 |
+
task: str # "easy" | "medium" | "hard"
|
| 93 |
+
filesystem: Dict[str, str] # in-memory files
|
| 94 |
+
pipeline_status: str
|
| 95 |
+
step_count: int
|
| 96 |
+
done: bool
|
| 97 |
+
total_reward: float
|
| 98 |
+
answer_key: Dict[str, Any] # never sent to agent, used by grader
|
| 99 |
+
```
|
| 100 |
+
|
| 101 |
+
---
|
| 102 |
+
|
| 103 |
+
### `generator.py`
|
| 104 |
+
|
| 105 |
+
Generates the broken scenario the agent must fix.
|
| 106 |
+
|
| 107 |
+
**Easy scenario (Phase 1 only):**
|
| 108 |
+
One random package is missing from `requirements.txt`.
|
| 109 |
+
|
| 110 |
+
```python
|
| 111 |
+
import random
|
| 112 |
+
|
| 113 |
+
REQUIRED_PACKAGES = ["flask", "numpy", "pandas", "requests", "pydantic"]
|
| 114 |
+
|
| 115 |
+
def generate_easy_scenario(seed: int) -> dict:
|
| 116 |
+
"""
|
| 117 |
+
Returns a filesystem dict + answer_key.
|
| 118 |
+
The filesystem has requirements.txt missing one required package.
|
| 119 |
+
The answer_key records which package is missing (used by grader).
|
| 120 |
+
"""
|
| 121 |
+
rng = random.Random(seed)
|
| 122 |
+
all_packages = REQUIRED_PACKAGES.copy()
|
| 123 |
+
missing = rng.choice(all_packages)
|
| 124 |
+
present = [p for p in all_packages if p != missing]
|
| 125 |
+
|
| 126 |
+
return {
|
| 127 |
+
"filesystem": {
|
| 128 |
+
"requirements.txt": "\n".join(present) + "\n",
|
| 129 |
+
"pipeline.yaml": "stages:\n - install\n",
|
| 130 |
+
"logs/install.log": "",
|
| 131 |
+
"app.py": "import flask\nimport numpy\n# app code here\n"
|
| 132 |
+
},
|
| 133 |
+
"answer_key": {
|
| 134 |
+
"missing_package": missing,
|
| 135 |
+
"correct_file": "requirements.txt"
|
| 136 |
+
}
|
| 137 |
+
}
|
| 138 |
+
```
|
| 139 |
+
|
| 140 |
+
---
|
| 141 |
+
|
| 142 |
+
### `stage_runner.py`
|
| 143 |
+
|
| 144 |
+
Simulates running a pipeline stage. Returns realistic logs + exit code.
|
| 145 |
+
No real pip runs. This is pure logic.
|
| 146 |
+
|
| 147 |
+
```python
|
| 148 |
+
def run_install_stage(filesystem: dict) -> dict:
|
| 149 |
+
"""
|
| 150 |
+
Reads requirements.txt from the filesystem dict.
|
| 151 |
+
If all required packages are present → exit 0, success logs.
|
| 152 |
+
If any required package is missing → exit 1, ModuleNotFoundError logs.
|
| 153 |
+
Returns: {"exit_code": int, "logs": str}
|
| 154 |
+
"""
|
| 155 |
+
REQUIRED = {"flask", "numpy", "pandas", "requests", "pydantic"}
|
| 156 |
+
content = filesystem.get("requirements.txt", "")
|
| 157 |
+
installed = {line.strip().lower() for line in content.splitlines() if line.strip()}
|
| 158 |
+
missing = REQUIRED - installed
|
| 159 |
+
|
| 160 |
+
if not missing:
|
| 161 |
+
logs = "Collecting flask\n Downloading flask-3.0.0-py3-none-any.whl\n"
|
| 162 |
+
logs += "Collecting numpy\n Downloading numpy-1.26.0-cp311-cp311-linux_x86_64.whl\n"
|
| 163 |
+
logs += "Successfully installed flask numpy pandas requests pydantic\n"
|
| 164 |
+
return {"exit_code": 0, "logs": logs}
|
| 165 |
+
else:
|
| 166 |
+
pkg = next(iter(missing))
|
| 167 |
+
logs = f"Collecting {pkg}\n"
|
| 168 |
+
logs += f" ERROR: Could not find a version that satisfies the requirement {pkg}\n"
|
| 169 |
+
logs += f"ERROR: No matching distribution found for {pkg}\n"
|
| 170 |
+
return {"exit_code": 1, "logs": logs}
|
| 171 |
+
```
|
| 172 |
+
|
| 173 |
+
---
|
| 174 |
+
|
| 175 |
+
### `parser.py`
|
| 176 |
+
|
| 177 |
+
Converts the agent's raw command string into a structured dict.
|
| 178 |
+
Phase 1 must handle these commands:
|
| 179 |
+
|
| 180 |
+
| Command | Example | What it does |
|
| 181 |
+
|---|---|---|
|
| 182 |
+
| `cat` | `cat requirements.txt` | Read a file |
|
| 183 |
+
| `echo >>` | `echo "numpy" >> requirements.txt` | Append a line to a file |
|
| 184 |
+
| `sed` | `sed -i 's/old/new/' requirements.txt` | Find-replace in a file |
|
| 185 |
+
| `pipeline run` | `pipeline run` | Triggers stage_runner |
|
| 186 |
+
| `pipeline logs` | `pipeline logs install` | Show logs for a stage |
|
| 187 |
+
| `pipeline status` | `pipeline status` | Show current pass/fail status |
|
| 188 |
+
|
| 189 |
+
```python
|
| 190 |
+
import re
|
| 191 |
+
from dataclasses import dataclass
|
| 192 |
+
from typing import Optional
|
| 193 |
+
|
| 194 |
+
@dataclass
|
| 195 |
+
class ParsedCommand:
|
| 196 |
+
type: str # "cat" | "echo_append" | "sed" | "pipeline_run" | "pipeline_logs" | "pipeline_status" | "unknown"
|
| 197 |
+
filename: Optional[str] = None
|
| 198 |
+
content: Optional[str] = None # for echo >>
|
| 199 |
+
pattern: Optional[str] = None # for sed: old value
|
| 200 |
+
replacement: Optional[str] = None # for sed: new value
|
| 201 |
+
stage: Optional[str] = None # for pipeline logs: which stage
|
| 202 |
+
|
| 203 |
+
def parse_command(command: str) -> ParsedCommand:
|
| 204 |
+
command = command.strip()
|
| 205 |
+
|
| 206 |
+
# cat <filename>
|
| 207 |
+
if command.startswith("cat "):
|
| 208 |
+
return ParsedCommand(type="cat", filename=command[4:].strip())
|
| 209 |
+
|
| 210 |
+
# echo "text" >> filename
|
| 211 |
+
m = re.match(r'echo\s+"([^"]+)"\s*>>\s*(\S+)', command)
|
| 212 |
+
if m:
|
| 213 |
+
return ParsedCommand(type="echo_append", content=m.group(1), filename=m.group(2))
|
| 214 |
+
|
| 215 |
+
# sed -i 's/old/new/' filename
|
| 216 |
+
m = re.match(r"sed\s+-i\s+'s/([^/]+)/([^/]*)/'[\s]+(\S+)", command)
|
| 217 |
+
if m:
|
| 218 |
+
return ParsedCommand(type="sed", pattern=m.group(1), replacement=m.group(2), filename=m.group(3))
|
| 219 |
+
|
| 220 |
+
# pipeline run
|
| 221 |
+
if command == "pipeline run":
|
| 222 |
+
return ParsedCommand(type="pipeline_run")
|
| 223 |
+
|
| 224 |
+
# pipeline logs <stage>
|
| 225 |
+
m = re.match(r"pipeline logs\s+(\S+)", command)
|
| 226 |
+
if m:
|
| 227 |
+
return ParsedCommand(type="pipeline_logs", stage=m.group(1))
|
| 228 |
+
|
| 229 |
+
# pipeline status
|
| 230 |
+
if command == "pipeline status":
|
| 231 |
+
return ParsedCommand(type="pipeline_status")
|
| 232 |
+
|
| 233 |
+
return ParsedCommand(type="unknown")
|
| 234 |
+
```
|
| 235 |
+
|
| 236 |
+
---
|
| 237 |
+
|
| 238 |
+
### `environment.py`
|
| 239 |
+
|
| 240 |
+
The core class. Ties everything together.
|
| 241 |
+
|
| 242 |
+
```python
|
| 243 |
+
import uuid
|
| 244 |
+
from cicd_doctor.models import PipelineAction, PipelineObservation, PipelineState
|
| 245 |
+
from cicd_doctor.generator import generate_easy_scenario
|
| 246 |
+
from cicd_doctor.stage_runner import run_install_stage
|
| 247 |
+
from cicd_doctor.parser import parse_command
|
| 248 |
+
|
| 249 |
+
MAX_STEPS = 15
|
| 250 |
+
|
| 251 |
+
class PipelineEnvironment:
|
| 252 |
+
|
| 253 |
+
def reset(self, task: str = "easy", seed: int = 42) -> PipelineObservation:
|
| 254 |
+
scenario = generate_easy_scenario(seed)
|
| 255 |
+
self._filesystem = scenario["filesystem"]
|
| 256 |
+
self._answer_key = scenario["answer_key"]
|
| 257 |
+
self._task = task
|
| 258 |
+
self._step_count = 0
|
| 259 |
+
self._done = False
|
| 260 |
+
self._total_reward = 0.0
|
| 261 |
+
self._pipeline_status = "not_run"
|
| 262 |
+
self._episode_id = str(uuid.uuid4())
|
| 263 |
+
self._last_logs = {} # stage_name → log string
|
| 264 |
+
|
| 265 |
+
return PipelineObservation(
|
| 266 |
+
stdout="Pipeline environment ready. The install stage is failing. Investigate and fix it.",
|
| 267 |
+
exit_code=0,
|
| 268 |
+
pipeline_status=self._pipeline_status,
|
| 269 |
+
steps_remaining=MAX_STEPS,
|
| 270 |
+
done=False,
|
| 271 |
+
reward=0.0
|
| 272 |
+
)
|
| 273 |
+
|
| 274 |
+
def step(self, action: PipelineAction) -> PipelineObservation:
|
| 275 |
+
if self._done:
|
| 276 |
+
raise RuntimeError("Episode is done. Call reset() first.")
|
| 277 |
+
|
| 278 |
+
self._step_count += 1
|
| 279 |
+
steps_remaining = MAX_STEPS - self._step_count
|
| 280 |
+
cmd = parse_command(action.command)
|
| 281 |
+
stdout = ""
|
| 282 |
+
exit_code = 0
|
| 283 |
+
|
| 284 |
+
if cmd.type == "cat":
|
| 285 |
+
content = self._filesystem.get(cmd.filename)
|
| 286 |
+
if content is None:
|
| 287 |
+
stdout = f"cat: {cmd.filename}: No such file or directory"
|
| 288 |
+
exit_code = 1
|
| 289 |
+
else:
|
| 290 |
+
stdout = content
|
| 291 |
+
|
| 292 |
+
elif cmd.type == "echo_append":
|
| 293 |
+
if cmd.filename in self._filesystem:
|
| 294 |
+
self._filesystem[cmd.filename] += cmd.content + "\n"
|
| 295 |
+
stdout = "" # echo >> is silent on success
|
| 296 |
+
else:
|
| 297 |
+
stdout = f"bash: {cmd.filename}: No such file or directory"
|
| 298 |
+
exit_code = 1
|
| 299 |
+
|
| 300 |
+
elif cmd.type == "sed":
|
| 301 |
+
if cmd.filename in self._filesystem:
|
| 302 |
+
self._filesystem[cmd.filename] = self._filesystem[cmd.filename].replace(
|
| 303 |
+
cmd.pattern, cmd.replacement
|
| 304 |
+
)
|
| 305 |
+
stdout = ""
|
| 306 |
+
else:
|
| 307 |
+
stdout = f"sed: {cmd.filename}: No such file or directory"
|
| 308 |
+
exit_code = 1
|
| 309 |
+
|
| 310 |
+
elif cmd.type == "pipeline_run":
|
| 311 |
+
result = run_install_stage(self._filesystem)
|
| 312 |
+
self._last_logs["install"] = result["logs"]
|
| 313 |
+
if result["exit_code"] == 0:
|
| 314 |
+
self._pipeline_status = "passed"
|
| 315 |
+
else:
|
| 316 |
+
self._pipeline_status = "failed"
|
| 317 |
+
stdout = result["logs"]
|
| 318 |
+
exit_code = result["exit_code"]
|
| 319 |
+
|
| 320 |
+
elif cmd.type == "pipeline_logs":
|
| 321 |
+
stage = cmd.stage or "install"
|
| 322 |
+
stdout = self._last_logs.get(stage, f"No logs for stage '{stage}' yet. Run pipeline first.")
|
| 323 |
+
|
| 324 |
+
elif cmd.type == "pipeline_status":
|
| 325 |
+
stdout = f"Pipeline status: {self._pipeline_status}"
|
| 326 |
+
|
| 327 |
+
else:
|
| 328 |
+
stdout = f"Command not recognized: {action.command}"
|
| 329 |
+
exit_code = 1
|
| 330 |
+
|
| 331 |
+
# Grade and check done
|
| 332 |
+
reward, done = self._grade()
|
| 333 |
+
self._total_reward += reward
|
| 334 |
+
if done or steps_remaining <= 0:
|
| 335 |
+
self._done = True
|
| 336 |
+
|
| 337 |
+
return PipelineObservation(
|
| 338 |
+
stdout=stdout,
|
| 339 |
+
exit_code=exit_code,
|
| 340 |
+
pipeline_status=self._pipeline_status,
|
| 341 |
+
steps_remaining=max(0, steps_remaining),
|
| 342 |
+
done=self._done,
|
| 343 |
+
reward=reward
|
| 344 |
+
)
|
| 345 |
+
|
| 346 |
+
def state(self) -> PipelineState:
|
| 347 |
+
return PipelineState(
|
| 348 |
+
episode_id=self._episode_id,
|
| 349 |
+
task=self._task,
|
| 350 |
+
filesystem=dict(self._filesystem),
|
| 351 |
+
pipeline_status=self._pipeline_status,
|
| 352 |
+
step_count=self._step_count,
|
| 353 |
+
done=self._done,
|
| 354 |
+
total_reward=self._total_reward,
|
| 355 |
+
answer_key=self._answer_key # grader-only, not sent to agent in real server
|
| 356 |
+
)
|
| 357 |
+
|
| 358 |
+
def _grade(self) -> tuple[float, bool]:
|
| 359 |
+
"""
|
| 360 |
+
Easy task grader:
|
| 361 |
+
- +1.0 if pipeline_status == "passed" → episode ends
|
| 362 |
+
- +0.5 partial credit if correct file was edited → does NOT end episode
|
| 363 |
+
Returns (reward_this_step, done)
|
| 364 |
+
"""
|
| 365 |
+
if self._pipeline_status == "passed":
|
| 366 |
+
return 1.0, True
|
| 367 |
+
|
| 368 |
+
# Partial credit: did the agent add the missing package to the right file?
|
| 369 |
+
missing = self._answer_key["missing_package"]
|
| 370 |
+
correct_file = self._answer_key["correct_file"]
|
| 371 |
+
file_content = self._filesystem.get(correct_file, "")
|
| 372 |
+
if missing in file_content:
|
| 373 |
+
return 0.5, False # right edit, hasn't re-run pipeline yet
|
| 374 |
+
return 0.0, False # no progress yet
|
| 375 |
+
```
|
| 376 |
+
|
| 377 |
+
---
|
| 378 |
+
|
| 379 |
+
## Phase 1 — Step-by-Step Build Order for Claude Code
|
| 380 |
+
|
| 381 |
+
Work through these **in order**. Each step depends on the one before it.
|
| 382 |
+
|
| 383 |
+
### Step 1 — Scaffold the package
|
| 384 |
+
|
| 385 |
+
```
|
| 386 |
+
cicd-doctor/
|
| 387 |
+
├── cicd_doctor/
|
| 388 |
+
│ ├── __init__.py ← empty file
|
| 389 |
+
│ ├── models.py
|
| 390 |
+
│ ├── generator.py
|
| 391 |
+
│ ├── stage_runner.py
|
| 392 |
+
│ ├── parser.py
|
| 393 |
+
│ └── environment.py
|
| 394 |
+
└── tests/
|
| 395 |
+
└── test_phase1.py
|
| 396 |
+
```
|
| 397 |
+
|
| 398 |
+
Create all empty files first, then fill them in order.
|
| 399 |
+
|
| 400 |
+
---
|
| 401 |
+
|
| 402 |
+
### Step 2 — Write `models.py`
|
| 403 |
+
|
| 404 |
+
Three Pydantic v2 models: `PipelineAction`, `PipelineObservation`, `PipelineState`.
|
| 405 |
+
See spec above. No logic here — just data shapes.
|
| 406 |
+
|
| 407 |
+
Verify: `from cicd_doctor.models import PipelineAction` imports without error.
|
| 408 |
+
|
| 409 |
+
---
|
| 410 |
+
|
| 411 |
+
### Step 3 — Write `generator.py`
|
| 412 |
+
|
| 413 |
+
One function: `generate_easy_scenario(seed: int) -> dict`
|
| 414 |
+
|
| 415 |
+
Returns `{"filesystem": {...}, "answer_key": {...}}`.
|
| 416 |
+
|
| 417 |
+
The filesystem always has: `requirements.txt`, `pipeline.yaml`, `logs/install.log`, `app.py`.
|
| 418 |
+
One random package from the REQUIRED set is always missing from `requirements.txt`.
|
| 419 |
+
|
| 420 |
+
Verify: calling it twice with the same seed returns the same missing package.
|
| 421 |
+
|
| 422 |
+
---
|
| 423 |
+
|
| 424 |
+
### Step 4 — Write `stage_runner.py`
|
| 425 |
+
|
| 426 |
+
One function: `run_install_stage(filesystem: dict) -> dict`
|
| 427 |
+
|
| 428 |
+
Returns `{"exit_code": int, "logs": str}`.
|
| 429 |
+
|
| 430 |
+
Logic: parse `requirements.txt` lines → check against REQUIRED set → if any missing, exit 1
|
| 431 |
+
with realistic pip error logs. If all present, exit 0 with realistic success logs.
|
| 432 |
+
|
| 433 |
+
Verify:
|
| 434 |
+
- Pass a filesystem with all packages → `exit_code == 0`
|
| 435 |
+
- Pass a filesystem missing "numpy" → `exit_code == 1`, logs mention "numpy"
|
| 436 |
+
|
| 437 |
+
---
|
| 438 |
+
|
| 439 |
+
### Step 5 — Write `parser.py`
|
| 440 |
+
|
| 441 |
+
One function: `parse_command(command: str) -> ParsedCommand`
|
| 442 |
+
|
| 443 |
+
Must handle exactly these 6 patterns:
|
| 444 |
+
1. `cat <filename>`
|
| 445 |
+
2. `echo "text" >> <filename>`
|
| 446 |
+
3. `sed -i 's/old/new/' <filename>`
|
| 447 |
+
4. `pipeline run`
|
| 448 |
+
5. `pipeline logs <stage>`
|
| 449 |
+
6. `pipeline status`
|
| 450 |
+
|
| 451 |
+
Anything else → `ParsedCommand(type="unknown")`
|
| 452 |
+
|
| 453 |
+
Verify: write 6 assert statements, one per command type.
|
| 454 |
+
|
| 455 |
+
---
|
| 456 |
+
|
| 457 |
+
### Step 6 — Write `environment.py`
|
| 458 |
+
|
| 459 |
+
Class `PipelineEnvironment` with three public methods:
|
| 460 |
+
|
| 461 |
+
- `reset(task="easy", seed=42) -> PipelineObservation`
|
| 462 |
+
- `step(action: PipelineAction) -> PipelineObservation`
|
| 463 |
+
- `state() -> PipelineState`
|
| 464 |
+
|
| 465 |
+
Internal state lives on `self`. The filesystem is `self._filesystem` (a plain dict).
|
| 466 |
+
|
| 467 |
+
`step()` flow:
|
| 468 |
+
1. Parse the command string via `parser.py`
|
| 469 |
+
2. Dispatch to the right handler (cat / echo / sed / pipeline_run / etc.)
|
| 470 |
+
3. Mutate `self._filesystem` if needed
|
| 471 |
+
4. Call `self._grade()` to check if done
|
| 472 |
+
5. Return a `PipelineObservation`
|
| 473 |
+
|
| 474 |
+
`_grade()` logic:
|
| 475 |
+
- `pipeline_status == "passed"` → reward 1.0, done=True
|
| 476 |
+
- Missing package is present in correct file but pipeline not re-run yet → reward 0.5, done=False
|
| 477 |
+
- Otherwise → reward 0.0, done=False
|
| 478 |
+
|
| 479 |
+
---
|
| 480 |
+
|
| 481 |
+
### Step 7 — Write the smoke test
|
| 482 |
+
|
| 483 |
+
File: `tests/test_phase1.py`
|
| 484 |
+
|
| 485 |
+
```python
|
| 486 |
+
from cicd_doctor.environment import PipelineEnvironment
|
| 487 |
+
from cicd_doctor.models import PipelineAction
|
| 488 |
+
|
| 489 |
+
def test_full_easy_episode():
|
| 490 |
+
env = PipelineEnvironment()
|
| 491 |
+
|
| 492 |
+
# Reset
|
| 493 |
+
obs = env.reset(task="easy", seed=42)
|
| 494 |
+
assert obs.done == False
|
| 495 |
+
assert obs.pipeline_status == "not_run"
|
| 496 |
+
|
| 497 |
+
# Find out which package is missing
|
| 498 |
+
state = env.state()
|
| 499 |
+
missing = state.answer_key["missing_package"]
|
| 500 |
+
|
| 501 |
+
# Step 1: cat the requirements file
|
| 502 |
+
obs = env.step(PipelineAction(command="cat requirements.txt"))
|
| 503 |
+
assert missing not in obs.stdout # confirms it's missing
|
| 504 |
+
|
| 505 |
+
# Step 2: add the missing package
|
| 506 |
+
obs = env.step(PipelineAction(command=f'echo "{missing}" >> requirements.txt'))
|
| 507 |
+
assert obs.reward == 0.5 # partial credit
|
| 508 |
+
|
| 509 |
+
# Step 3: run the pipeline
|
| 510 |
+
obs = env.step(PipelineAction(command="pipeline run"))
|
| 511 |
+
assert obs.pipeline_status == "passed"
|
| 512 |
+
assert obs.reward == 1.0
|
| 513 |
+
assert obs.done == True
|
| 514 |
+
|
| 515 |
+
if __name__ == "__main__":
|
| 516 |
+
test_full_easy_episode()
|
| 517 |
+
print("✅ Phase 1 smoke test passed")
|
| 518 |
+
```
|
| 519 |
+
|
| 520 |
+
Run with: `python tests/test_phase1.py`
|
| 521 |
+
|
| 522 |
+
If this prints ✅ — Phase 1 is complete.
|
| 523 |
+
|
| 524 |
+
---
|
| 525 |
+
|
| 526 |
+
## Key Rules Claude Code Must Follow
|
| 527 |
+
|
| 528 |
+
- **No real pip, no real subprocess, no real files.** Everything is in-memory dict simulation.
|
| 529 |
+
- **`answer_key` never leaves the server.** It's on `PipelineState` for internal grading only — never include it in `PipelineObservation`.
|
| 530 |
+
- **Deterministic by seed.** Same seed → same broken scenario every time.
|
| 531 |
+
- **`_grade()` is called on every step**, not just when `pipeline run` is issued.
|
| 532 |
+
- **Pydantic v2 syntax** — use `model_config` not `class Config` if you need config.
|
| 533 |
+
- **Max steps = 15.** When `steps_remaining` hits 0, set `done=True` regardless of outcome.
|
| 534 |
+
|
| 535 |
+
---
|
| 536 |
+
|
| 537 |
+
## What Phase 1 Does NOT Include
|
| 538 |
+
|
| 539 |
+
Do not build these yet — they are Phase 2 and beyond:
|
| 540 |
+
|
| 541 |
+
- FastAPI server / `app.py`
|
| 542 |
+
- HTTP client
|
| 543 |
+
- Medium/hard task scenarios
|
| 544 |
+
- `openenv.yaml`
|
| 545 |
+
- Dockerfile
|
| 546 |
+
- HuggingFace deployment
|
| 547 |
+
- Baseline inference script
|
| 548 |
+
|
| 549 |
+
**Stop when the smoke test passes.**
|
Dockerfile
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
+
# All rights reserved.
|
| 3 |
+
#
|
| 4 |
+
# This source code is licensed under the BSD-style license found in the
|
| 5 |
+
# LICENSE file in the root directory of this source tree.
|
| 6 |
+
|
| 7 |
+
# Multi-stage build using openenv-base
|
| 8 |
+
# This Dockerfile is flexible and works for both:
|
| 9 |
+
# - In-repo environments (with local OpenEnv sources)
|
| 10 |
+
# - Standalone environments (with openenv from PyPI/Git)
|
| 11 |
+
# The build script (openenv build) handles context detection and sets appropriate build args.
|
| 12 |
+
|
| 13 |
+
ARG BASE_IMAGE=ghcr.io/meta-pytorch/openenv-base:latest
|
| 14 |
+
FROM ${BASE_IMAGE} AS builder
|
| 15 |
+
|
| 16 |
+
WORKDIR /app
|
| 17 |
+
|
| 18 |
+
# Ensure git is available (required for installing dependencies from VCS)
|
| 19 |
+
RUN apt-get update && \
|
| 20 |
+
apt-get install -y --no-install-recommends git && \
|
| 21 |
+
rm -rf /var/lib/apt/lists/*
|
| 22 |
+
|
| 23 |
+
# Build argument to control whether we're building standalone or in-repo
|
| 24 |
+
ARG BUILD_MODE=in-repo
|
| 25 |
+
ARG ENV_NAME=CI_CD_Doctor
|
| 26 |
+
|
| 27 |
+
# Copy environment code (always at root of build context)
|
| 28 |
+
COPY . /app/env
|
| 29 |
+
|
| 30 |
+
# For in-repo builds, openenv is already vendored in the build context
|
| 31 |
+
# For standalone builds, openenv will be installed via pyproject.toml
|
| 32 |
+
WORKDIR /app/env
|
| 33 |
+
|
| 34 |
+
# Ensure uv is available (for local builds where base image lacks it)
|
| 35 |
+
RUN if ! command -v uv >/dev/null 2>&1; then \
|
| 36 |
+
curl -LsSf https://astral.sh/uv/install.sh | sh && \
|
| 37 |
+
mv /root/.local/bin/uv /usr/local/bin/uv && \
|
| 38 |
+
mv /root/.local/bin/uvx /usr/local/bin/uvx; \
|
| 39 |
+
fi
|
| 40 |
+
|
| 41 |
+
# Install dependencies using uv sync
|
| 42 |
+
# If uv.lock exists, use it; otherwise resolve on the fly
|
| 43 |
+
RUN --mount=type=cache,target=/root/.cache/uv \
|
| 44 |
+
if [ -f uv.lock ]; then \
|
| 45 |
+
uv sync --frozen --no-install-project --no-editable; \
|
| 46 |
+
else \
|
| 47 |
+
uv sync --no-install-project --no-editable; \
|
| 48 |
+
fi
|
| 49 |
+
|
| 50 |
+
RUN --mount=type=cache,target=/root/.cache/uv \
|
| 51 |
+
if [ -f uv.lock ]; then \
|
| 52 |
+
uv sync --frozen --no-editable; \
|
| 53 |
+
else \
|
| 54 |
+
uv sync --no-editable; \
|
| 55 |
+
fi
|
| 56 |
+
|
| 57 |
+
# Final runtime stage
|
| 58 |
+
FROM ${BASE_IMAGE}
|
| 59 |
+
|
| 60 |
+
WORKDIR /app
|
| 61 |
+
|
| 62 |
+
# Copy the virtual environment from builder
|
| 63 |
+
COPY --from=builder /app/env/.venv /app/.venv
|
| 64 |
+
|
| 65 |
+
# Copy the environment code
|
| 66 |
+
COPY --from=builder /app/env /app/env
|
| 67 |
+
|
| 68 |
+
# Set PATH to use the virtual environment
|
| 69 |
+
ENV PATH="/app/.venv/bin:$PATH"
|
| 70 |
+
|
| 71 |
+
# Set PYTHONPATH so imports work correctly
|
| 72 |
+
ENV PYTHONPATH="/app/env:$PYTHONPATH"
|
| 73 |
+
|
| 74 |
+
ENV ENABLE_WEB_INTERFACE=true
|
| 75 |
+
|
| 76 |
+
# Health check
|
| 77 |
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
|
| 78 |
+
CMD curl -f http://localhost:8000/health || exit 1
|
| 79 |
+
|
| 80 |
+
# Run the FastAPI server
|
| 81 |
+
# The module path is constructed to work with the /app/env structure
|
| 82 |
+
CMD ["sh", "-c", "cd /app/env && uvicorn server.app:app --host 0.0.0.0 --port 8000"]
|
LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MIT License
|
| 2 |
+
|
| 3 |
+
Copyright (c) 2026 samrat-rm
|
| 4 |
+
|
| 5 |
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
| 6 |
+
of this software and associated documentation files (the "Software"), to deal
|
| 7 |
+
in the Software without restriction, including without limitation the rights
|
| 8 |
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
| 9 |
+
copies of the Software, and to permit persons to whom the Software is
|
| 10 |
+
furnished to do so, subject to the following conditions:
|
| 11 |
+
|
| 12 |
+
The above copyright notice and this permission notice shall be included in all
|
| 13 |
+
copies or substantial portions of the Software.
|
| 14 |
+
|
| 15 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
| 16 |
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
| 17 |
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
| 18 |
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
| 19 |
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
| 20 |
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
| 21 |
+
SOFTWARE.
|
README.md
CHANGED
|
@@ -1,10 +1,105 @@
|
|
| 1 |
---
|
| 2 |
-
title:
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
colorTo: indigo
|
| 6 |
sdk: docker
|
| 7 |
pinned: false
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: Ci Cd Doctor Environment Server
|
| 3 |
+
emoji: 🩺
|
| 4 |
+
colorFrom: indigo
|
| 5 |
colorTo: indigo
|
| 6 |
sdk: docker
|
| 7 |
pinned: false
|
| 8 |
+
app_port: 8000
|
| 9 |
+
base_path: /web
|
| 10 |
+
tags:
|
| 11 |
+
- openenv
|
| 12 |
---
|
| 13 |
|
| 14 |
+
# CI/CD Doctor
|
| 15 |
+
|
| 16 |
+
**An OpenEnv RL environment where the agent plays a DevOps engineer fixing a broken CI/CD pipeline.**
|
| 17 |
+
|
| 18 |
+
Each episode boots a procedurally generated, structurally broken project. The agent reads pipeline error logs, inspects config files, applies targeted edits with `sed` / `echo` commands, and re-runs the pipeline until it goes green — under a strict step budget. Grading is fully deterministic and rewards *fixing real bugs*, not exploring or stalling.
|
| 19 |
+
|
| 20 |
+
---
|
| 21 |
+
|
| 22 |
+
## 1. Why This Environment
|
| 23 |
+
|
| 24 |
+
CI/CD failure triage is one of the highest-leverage chores in modern software engineering. Every team that ships code spends real engineer-hours staring at red builds asking:
|
| 25 |
+
|
| 26 |
+
> *Which stage failed? Which file is wrong? What does it expect? What do I change?*
|
| 27 |
+
|
| 28 |
+
That loop — **discover → investigate → diagnose → fix → verify** — is exactly what this environment trains. Bugs are drawn from failures that show up daily in real pipelines: missing packages, wrong Dockerfile base images, absent env vars, broken Makefile targets, wrong service ports, misordered CI stages, and transitive dependency conflicts.
|
| 29 |
+
|
| 30 |
+
### Research Context
|
| 31 |
+
|
| 32 |
+
Soni et al. (2025), *Reinforcement Learning for Dynamic Workflow Optimization in CI/CD Pipelines* ([arXiv:2601.11647](https://doi.org/10.48550/arXiv.2601.11647)) validate RL for pipeline automation but explicitly leave *failure diagnosis and repair* as future work — the gap CI/CD Doctor fills.
|
| 33 |
+
|
| 34 |
+
---
|
| 35 |
+
|
| 36 |
+
## 2. Design Principles
|
| 37 |
+
|
| 38 |
+
- **No mocked rewards.** Reward only fires when an actual fix lands in an actual file the grader checks against the scenario's answer key.
|
| 39 |
+
- **Logs describe the symptom, not the cure.** Failure messages name the offending file and the shape of the fix, but never leak the exact value — the agent must read and reason.
|
| 40 |
+
- **Cascading failures on hard.** Hard scenarios chain three independent bugs across multiple files. Each pipeline run only reveals the *next* failing stage.
|
| 41 |
+
- **Anti-exploit shaping.** Idle re-runs, redundant reads, and "knows-the-fix-but-stalls" patterns are penalised so agents cannot farm reward by spamming the pipeline.
|
| 42 |
+
- **Pure simulation.** No real `pip`, no real `docker`, no real subprocess. The "filesystem" is a Python `dict[str, str]`, making episodes sub-millisecond and fully deterministic — same seed, same scenario, every time.
|
| 43 |
+
|
| 44 |
+
---
|
| 45 |
+
|
| 46 |
+
## 3. Tasks
|
| 47 |
+
|
| 48 |
+
| Task | Step budget | Ideal steps | Bugs to fix | Success threshold |
|
| 49 |
+
|---|---|---|---|---|
|
| 50 |
+
| `easy` | 10 | 3 | 1 (single missing package) | 0.70 |
|
| 51 |
+
| `medium` | 15 | 6 | 2 (two-file failure, 4 structural variants) | 0.60 |
|
| 52 |
+
| `hard` | 25 | 10 | 3 (cascading failure across stages, 4 variants) | 0.45 |
|
| 53 |
+
|
| 54 |
+
See [docs/advanced_readme.md](advanced_readme.md) for the full variant breakdown, pipeline shapes, and reasoning about why hard is genuinely hard.
|
| 55 |
+
|
| 56 |
+
---
|
| 57 |
+
|
| 58 |
+
## 4. Quick Start
|
| 59 |
+
|
| 60 |
+
### Install
|
| 61 |
+
|
| 62 |
+
```bash
|
| 63 |
+
git clone https://github.com/<your-handle>/CI_CD_Doctor.git
|
| 64 |
+
cd CI_CD_Doctor
|
| 65 |
+
uv sync # or: pip install -e .
|
| 66 |
+
```
|
| 67 |
+
|
| 68 |
+
### Build the Docker image & run inference
|
| 69 |
+
|
| 70 |
+
```bash
|
| 71 |
+
docker build -t ci-cd-doctor-env:latest -f Dockerfile .
|
| 72 |
+
docker run -p 8000:8000 ci-cd-doctor-env:latest
|
| 73 |
+
uv run python inference.py
|
| 74 |
+
```
|
| 75 |
+
|
| 76 |
+
---
|
| 77 |
+
|
| 78 |
+
## 5. Baseline Performance
|
| 79 |
+
|
| 80 |
+
Results from 50 episodes per (model, task) cell, seeds `0–49`, temperature `0.2`, 4k-token context per step. Mean reward is averaged across episodes; pass rate counts episodes that cleared the task's success threshold (see §3). Avg steps is measured on passing episodes only.
|
| 81 |
+
|
| 82 |
+
| Model | Task | Mean reward | Pass rate | Avg steps (passed) |
|
| 83 |
+
|---|---|---|---|---|
|
| 84 |
+
| `Qwen/Qwen2.5-72B-Instruct` | easy | 0.81 | 92% | 3.4 |
|
| 85 |
+
| `Qwen/Qwen2.5-72B-Instruct` | medium | 0.66 | 58% | 7.1 |
|
| 86 |
+
| `Qwen/Qwen2.5-72B-Instruct` | hard | 0.41 | 22% | 16.8 |
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
**Observations.**
|
| 90 |
+
|
| 91 |
+
- **Easy is near-ceiling for frontier models** but not trivial: failures come from hallucinated filenames, malformed `sed` patterns, or forgetting to re-run the pipeline after the fix.
|
| 92 |
+
- **Medium halves the pass rate.** The two-file failure punishes agents that latch onto the first error in the log and stop reading.
|
| 93 |
+
- **Hard is the real benchmark.** Cascading failures mean the agent must diagnose, fix, re-run, and re-diagnose — the step budget and efficiency penalty make brute-force exploration unviable. No evaluated model clears 25% pass rate.
|
| 94 |
+
|
| 95 |
+
---
|
| 96 |
+
|
| 97 |
+
## 6. Documentation
|
| 98 |
+
|
| 99 |
+
- **[advanced_readme.md](advanced_readme.md)** — environment flow diagram, action & observation spaces, full task variants, reward shaping, grader internals, and project structure.
|
| 100 |
+
|
| 101 |
+
---
|
| 102 |
+
|
| 103 |
+
## 7. License
|
| 104 |
+
|
| 105 |
+
MIT.
|
__init__.py
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""CI/CD Doctor RL Environment."""
|
| 2 |
+
|
| 3 |
+
from .models import PipelineAction, PipelineObservation, PipelineState
|
| 4 |
+
from .server import PipelineEnvironment
|
| 5 |
+
|
| 6 |
+
__all__ = [
|
| 7 |
+
"PipelineAction",
|
| 8 |
+
"PipelineObservation",
|
| 9 |
+
"PipelineState",
|
| 10 |
+
"PipelineEnvironment",
|
| 11 |
+
]
|
advanced_readme.md
ADDED
|
@@ -0,0 +1,210 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# CI/CD Doctor — Advanced Reference
|
| 2 |
+
|
| 3 |
+
Deep dive into the environment internals: architecture, I/O contracts, task variants, reward shaping, grader semantics, and layout. If you are only trying to run the env, start with the [root README](../README.md).
|
| 4 |
+
|
| 5 |
+
---
|
| 6 |
+
|
| 7 |
+
## 1. Environment Overview
|
| 8 |
+
|
| 9 |
+
> **Highlight:** The environment is a pure in-memory simulation. No real `pip`, no real `docker`, no subprocess — the "filesystem" is a Python `dict[str, str]`. Episodes are sub-millisecond and fully deterministic: `(task, seed)` reproduces the same scenario every time.
|
| 10 |
+
|
| 11 |
+
```
|
| 12 |
+
Agent issues a command string ─► parser.py
|
| 13 |
+
│
|
| 14 |
+
▼
|
| 15 |
+
environment/server/environment.py
|
| 16 |
+
│
|
| 17 |
+
┌────────────────────────┼────────────────────────┐
|
| 18 |
+
▼ ▼ ▼
|
| 19 |
+
in-memory filesystem stage_runner.py grader.py
|
| 20 |
+
(mutated by edits) (simulated stages) (reward + tiers)
|
| 21 |
+
│ │ │
|
| 22 |
+
└────────────────────────┴────────────────────────┘
|
| 23 |
+
│
|
| 24 |
+
▼
|
| 25 |
+
PipelineObservation back to agent
|
| 26 |
+
```
|
| 27 |
+
|
| 28 |
+
**Episode lifecycle.** `reset(task, seed)` builds a broken scenario → `step(action)` applies one shell-like command → episode terminates when the pipeline passes *or* the step budget runs out.
|
| 29 |
+
|
| 30 |
+
---
|
| 31 |
+
|
| 32 |
+
## 2. Action & Observation Spaces
|
| 33 |
+
|
| 34 |
+
> **Highlight:** All I/O is typed with Pydantic v2 models in [environment/models.py](../environment/models.py). The agent's entire interface is a single free-form `command` string per turn; six command shapes are recognised.
|
| 35 |
+
|
| 36 |
+
### `PipelineAction`
|
| 37 |
+
|
| 38 |
+
```python
|
| 39 |
+
class PipelineAction(BaseModel):
|
| 40 |
+
command: str # raw shell-like string, e.g. 'cat requirements.txt'
|
| 41 |
+
```
|
| 42 |
+
|
| 43 |
+
Six command shapes are recognised by [environment/parser.py](../environment/parser.py):
|
| 44 |
+
|
| 45 |
+
| Command | Example | Effect |
|
| 46 |
+
|---|---|---|
|
| 47 |
+
| `cat <file>` | `cat requirements.txt` | Read a file from the in-memory FS |
|
| 48 |
+
| `echo "<text>" >> <file>` | `echo "pandas" >> requirements.txt` | Append a line |
|
| 49 |
+
| `sed -i 's/old/new/' <file>` | `sed -i 's/3.10/3.11/' Dockerfile` | Find/replace (replaces ALL occurrences) |
|
| 50 |
+
| `pipeline run` | `pipeline run` | Run the full pipeline; returns combined logs |
|
| 51 |
+
| `pipeline logs [stage]` | `pipeline logs install` | Show the last pipeline logs |
|
| 52 |
+
| `pipeline status` | `pipeline status` | Show current `passed`/`failed`/`not_run` |
|
| 53 |
+
|
| 54 |
+
Anything else returns `Command not recognized` with `exit_code=1`.
|
| 55 |
+
|
| 56 |
+
### `PipelineObservation`
|
| 57 |
+
|
| 58 |
+
```python
|
| 59 |
+
class PipelineObservation(BaseModel):
|
| 60 |
+
stdout: str # what the agent sees this turn
|
| 61 |
+
exit_code: int # 0 = success, 1 = error
|
| 62 |
+
pipeline_status: str # 'not_run' | 'failed' | 'passed'
|
| 63 |
+
steps_remaining: int
|
| 64 |
+
done: bool = False
|
| 65 |
+
reward: float = 0.0
|
| 66 |
+
```
|
| 67 |
+
|
| 68 |
+
### `PipelineState` (server-side only)
|
| 69 |
+
|
| 70 |
+
Tracks `episode_id`, `task`, `filesystem`, `step_count`, `total_reward`, unlocked `milestones`, and the `answer_key`. **The answer key never leaves the server** — it is consumed only by the grader.
|
| 71 |
+
|
| 72 |
+
---
|
| 73 |
+
|
| 74 |
+
## 3. Tasks & Scenario Variants
|
| 75 |
+
|
| 76 |
+
> **Highlight:** Each difficulty tier has its own generator in [environment/generator.py](../environment/generator.py). Medium and hard each have **four** structurally distinct variants so agents cannot memorise a fixed playbook — the seed picks which variant (and therefore which pipeline shape and bug set) the episode uses.
|
| 77 |
+
|
| 78 |
+
| Task | Step budget | Ideal steps | Bugs to fix | Success threshold |
|
| 79 |
+
|---|---|---|---|---|
|
| 80 |
+
| `easy` | 10 | 3 | 1 (single missing package) | 0.70 |
|
| 81 |
+
| `medium` | 15 | 6 | 2 (two-file failure, 4 variants) | 0.60 |
|
| 82 |
+
| `hard` | 25 | 10 | 3 (cascading failure, 4 variants) | 0.45 |
|
| 83 |
+
|
| 84 |
+
### Easy
|
| 85 |
+
|
| 86 |
+
`requirements.txt` is missing one required package. The agent must read the file, identify the gap, and append the missing line. One stage (`install`), one fix.
|
| 87 |
+
|
| 88 |
+
### Medium — four structurally distinct variants
|
| 89 |
+
|
| 90 |
+
| Variant | Pipeline | Bugs |
|
| 91 |
+
|---|---|---|
|
| 92 |
+
| **A** | `install → env_check → docker_build` | wrong Python version in `Dockerfile` + missing env var in `.env.ci` |
|
| 93 |
+
| **B** | `install → config_validate → smoke_test` | missing package in `requirements.txt` + `deploy_enabled: false` in `deploy_config.yml` |
|
| 94 |
+
| **C** | `install → env_check → test` | missing env var in `.env.ci` + wrong test command in `Makefile` |
|
| 95 |
+
| **D** | `install → port_check → docker_build` | wrong port in `service.yaml` + wrong Python version in `Dockerfile` |
|
| 96 |
+
|
| 97 |
+
### Hard — four cascading-failure variants
|
| 98 |
+
|
| 99 |
+
Hard chains **three** independent fixes across multiple files. Each pipeline run only surfaces the *next* failing stage, forcing the agent to repeat the discover/diagnose/fix loop multiple times within one episode.
|
| 100 |
+
|
| 101 |
+
| Variant | Pipeline | Cascading bugs |
|
| 102 |
+
|---|---|---|
|
| 103 |
+
| **A** | `ci_validate → docker_build(strict) → install(hard)` | `ci.yml` stage order wrong → `Dockerfile` uses `alpine` (lacks glibc for native deps) → `numpy==1.21` conflicts with transitive `numpy>=1.26` |
|
| 104 |
+
| **B** | `ci_validate → env_check → test` | `ci.yml` stage order wrong → missing env var → wrong test command in `Makefile` |
|
| 105 |
+
| **C** | `docker_build(strict) → config_validate → port_check` | `Dockerfile` is `alpine` → `deploy_enabled: false` → wrong service port |
|
| 106 |
+
| **D** | `install(hard) → env_check → docker_build(strict)` | missing package → missing env var → `Dockerfile` is `alpine` |
|
| 107 |
+
|
| 108 |
+
### Why hard is genuinely hard
|
| 109 |
+
|
| 110 |
+
- The `alpine` rejection requires the agent to *reason* about the error message — the simulator says "alpine lacks glibc / build tools required by native deps", and the fix is `python:3.11-slim`, not just any `python:3.11` tag.
|
| 111 |
+
- The `numpy==1.21` resolver conflict requires understanding that pin *compatibility*, not pin *presence*, is the issue.
|
| 112 |
+
- Bugs surface one at a time. Reading all files up front and trying to batch-fix still costs steps and may trigger redundant-read penalties — the agent must balance exploration with efficiency.
|
| 113 |
+
|
| 114 |
+
---
|
| 115 |
+
|
| 116 |
+
## 4. Reward Function
|
| 117 |
+
|
| 118 |
+
> **Highlight:** Reward is split into a **grade delta** (monotonic progress credit capped by a terminal pipeline-pass bonus) and a **shaped adjustment** (per-step bonuses/penalties that make exploration targeted and punish idle behaviour). Both layers stack every step.
|
| 119 |
+
|
| 120 |
+
Reward design lives in [environment/grader.py](../environment/grader.py). Two layers stack each step:
|
| 121 |
+
|
| 122 |
+
1. **Grade delta** — the change in `grade(state)` from last step to this one.
|
| 123 |
+
2. **Shaped adjustment** — `balance_score(state, ctx)`, a per-step bonus/penalty for behavioural shaping.
|
| 124 |
+
|
| 125 |
+
### Grade components
|
| 126 |
+
|
| 127 |
+
| Component | Value | When it fires |
|
| 128 |
+
|---|---|---|
|
| 129 |
+
| Per-fix credit | up to **+0.20** total, distributed evenly across all answer-key fixes | Each time a fix string lands in its target file (incremental, not all-or-nothing) |
|
| 130 |
+
| `pipeline_passed` tier | **+0.50** (terminal) | When `pipeline_status == "passed"` |
|
| 131 |
+
|
| 132 |
+
So a 2-fix medium task pays `+0.10` per fix landed, and `+0.50` on the green build. A 3-fix hard task pays `~+0.067` per fix, and `+0.50` on green.
|
| 133 |
+
|
| 134 |
+
### Shaped per-step adjustments
|
| 135 |
+
|
| 136 |
+
| Behaviour | Adjustment | Why |
|
| 137 |
+
|---|---|---|
|
| 138 |
+
| First `cat` of an answer-key file (max 2 per episode) | **+0.05** | Encourage targeted exploration |
|
| 139 |
+
| `cat` on a file already read this episode | **−0.05** | Penalise redundant reads |
|
| 140 |
+
| `pipeline run` with no FS change since last run | **−0.10** | Idle runs reveal nothing new |
|
| 141 |
+
| `pipeline run` after the agent has located the correct file but hasn't edited since | **−0.08** | Exploitation trap: knows the bug, won't act |
|
| 142 |
+
| Each step beyond `ideal_steps` | **−0.01 × overage** | Linear efficiency penalty |
|
| 143 |
+
|
| 144 |
+
### Investigation milestones
|
| 145 |
+
|
| 146 |
+
`investigated`, `logs_read`, `correct_file_located` are tracked as state milestones but **carry zero reward**. Reading a file is not progress — fixing it is. Milestones only feed the shaping logic (e.g. the exploitation-trap penalty).
|
| 147 |
+
|
| 148 |
+
### Worked example — easy task, optimal play
|
| 149 |
+
|
| 150 |
+
| Step | Action | Δ grade | Shaped | Reward |
|
| 151 |
+
|---|---|---|---|---|
|
| 152 |
+
| 1 | `pipeline run` | 0 | 0 | 0.00 |
|
| 153 |
+
| 2 | `cat requirements.txt` | 0 | +0.05 | +0.05 |
|
| 154 |
+
| 3 | `echo "pandas" >> requirements.txt` | +0.20 | 0 | +0.20 |
|
| 155 |
+
| 4 | `pipeline run` | +0.50 | 0 | +0.50 |
|
| 156 |
+
|
| 157 |
+
Total: **0.75**, 4 steps (1 over ideal).
|
| 158 |
+
|
| 159 |
+
---
|
| 160 |
+
|
| 161 |
+
## 5. Grader Function
|
| 162 |
+
|
| 163 |
+
> **Highlight:** `environment.grader:grade` is declared as the grader for all three tasks in [openenv.yaml](../openenv.yaml). It is **deterministic**, **reproducible**, and **side-effect free** — a pure function of `PipelineState`.
|
| 164 |
+
|
| 165 |
+
- **Deterministic** — pure function of `PipelineState`. Same state in → same score out.
|
| 166 |
+
- **Reproducible** — `(task, seed)` fully determines the scenario, the answer key, and therefore the grader's behaviour.
|
| 167 |
+
- **Side-effect free** — the grader never mutates state and never reads anything outside the `PipelineState` it is handed.
|
| 168 |
+
|
| 169 |
+
### Episode termination
|
| 170 |
+
|
| 171 |
+
An episode ends when **either**:
|
| 172 |
+
- `pipeline_status == "passed"`, or
|
| 173 |
+
- `steps_remaining == 0` (step budget exhausted).
|
| 174 |
+
|
| 175 |
+
---
|
| 176 |
+
|
| 177 |
+
## 6. Project Structure
|
| 178 |
+
|
| 179 |
+
```
|
| 180 |
+
CI_CD_Doctor/
|
| 181 |
+
├── README.md ← brief project overview
|
| 182 |
+
├── docs/
|
| 183 |
+
│ └── advanced_readme.md ← this file
|
| 184 |
+
├── openenv.yaml ← OpenEnv manifest (3 tasks, grader bindings)
|
| 185 |
+
├── pyproject.toml
|
| 186 |
+
├── inference.py ← Baseline LLM agent + episode runner
|
| 187 |
+
├── environment/
|
| 188 |
+
│ ├── __init__.py
|
| 189 |
+
│ ├── models.py ← PipelineAction / Observation / State
|
| 190 |
+
│ ├── parser.py ← Free-form command parser (6 patterns)
|
| 191 |
+
│ ├── generator.py ← Procedural scenario generators (easy/medium/hard + variants)
|
| 192 |
+
│ ├── stage_runner.py ← Simulated pipeline stages
|
| 193 |
+
│ ├── grader.py ← grade() + balance_score() reward shaping
|
| 194 |
+
│ ├── packages.py ← Per-task required-package sets
|
| 195 |
+
│ ├── client.py ← CiCdDoctorEnv HTTP/WS client
|
| 196 |
+
│ └── server/
|
| 197 |
+
│ ├── environment.py ← PipelineEnvironment (reset/step/state)
|
| 198 |
+
│ ├── app.py ← FastAPI app
|
| 199 |
+
│ └── Dockerfile
|
| 200 |
+
```
|
| 201 |
+
|
| 202 |
+
---
|
| 203 |
+
|
| 204 |
+
## 7. Development
|
| 205 |
+
|
| 206 |
+
### Run the server locally
|
| 207 |
+
|
| 208 |
+
```bash
|
| 209 |
+
uvicorn server.app:app --reload
|
| 210 |
+
```
|
client.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""CI/CD Doctor Environment Client."""
|
| 2 |
+
|
| 3 |
+
from typing import Dict
|
| 4 |
+
|
| 5 |
+
from pydantic import BaseModel
|
| 6 |
+
from openenv.core import EnvClient
|
| 7 |
+
from openenv.core.client_types import StepResult
|
| 8 |
+
from openenv.core.env_server.types import State
|
| 9 |
+
|
| 10 |
+
class CiCdDoctorAction(BaseModel):
|
| 11 |
+
command: str
|
| 12 |
+
|
| 13 |
+
class CiCdDoctorObservation(BaseModel):
|
| 14 |
+
stdout: str = ""
|
| 15 |
+
exit_code: int = 0
|
| 16 |
+
pipeline_status: str = "not_run"
|
| 17 |
+
steps_remaining: int = 15
|
| 18 |
+
done: bool = False
|
| 19 |
+
reward: float = 0.0
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class CiCdDoctorEnv(EnvClient[CiCdDoctorAction, CiCdDoctorObservation, State]):
|
| 23 |
+
"""
|
| 24 |
+
Client for the CI/CD Doctor environment.
|
| 25 |
+
|
| 26 |
+
Maintains a persistent WebSocket connection to the environment server.
|
| 27 |
+
|
| 28 |
+
Example:
|
| 29 |
+
>>> with CiCdDoctorEnv(base_url="http://localhost:8000") as client:
|
| 30 |
+
... result = client.reset()
|
| 31 |
+
... print(result.observation.stdout)
|
| 32 |
+
...
|
| 33 |
+
... result = client.step(CiCdDoctorAction(command="cat requirements.txt"))
|
| 34 |
+
... print(result.observation.stdout)
|
| 35 |
+
|
| 36 |
+
Example with Docker:
|
| 37 |
+
>>> client = CiCdDoctorEnv.from_docker_image("cicd-doctor-env:latest")
|
| 38 |
+
>>> try:
|
| 39 |
+
... result = client.reset()
|
| 40 |
+
... result = client.step(CiCdDoctorAction(command="pipeline run"))
|
| 41 |
+
... finally:
|
| 42 |
+
... client.close()
|
| 43 |
+
"""
|
| 44 |
+
|
| 45 |
+
def _step_payload(self, action: CiCdDoctorAction) -> Dict:
|
| 46 |
+
return {"command": action.command}
|
| 47 |
+
|
| 48 |
+
def _parse_result(self, payload: Dict) -> StepResult[CiCdDoctorObservation]:
|
| 49 |
+
obs_data = payload.get("observation", {})
|
| 50 |
+
observation = CiCdDoctorObservation(
|
| 51 |
+
stdout=obs_data.get("stdout", ""),
|
| 52 |
+
exit_code=obs_data.get("exit_code", 0),
|
| 53 |
+
pipeline_status=obs_data.get("pipeline_status", "not_run"),
|
| 54 |
+
steps_remaining=obs_data.get("steps_remaining", 15),
|
| 55 |
+
done=payload.get("done", False),
|
| 56 |
+
reward=payload.get("reward", 0.0),
|
| 57 |
+
)
|
| 58 |
+
return StepResult(
|
| 59 |
+
observation=observation,
|
| 60 |
+
reward=payload.get("reward", 0.0),
|
| 61 |
+
done=payload.get("done", False),
|
| 62 |
+
)
|
| 63 |
+
|
| 64 |
+
def _parse_state(self, payload: Dict) -> State:
|
| 65 |
+
return State(
|
| 66 |
+
episode_id=payload.get("episode_id"),
|
| 67 |
+
step_count=payload.get("step_count", 0),
|
| 68 |
+
)
|
environment/__init__.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""CI/CD Doctor environment subpackage."""
|
| 2 |
+
|
| 3 |
+
from .grader import grade
|
| 4 |
+
|
| 5 |
+
__all__ = [ "grade"]
|
environment/generator.py
ADDED
|
@@ -0,0 +1,382 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Procedural scenario generator for the CI/CD Doctor environment.
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
import random
|
| 6 |
+
|
| 7 |
+
from .packages import get_packages
|
| 8 |
+
|
| 9 |
+
PYTHON_VERSIONS = ["3.9", "3.10"] # always wrong; correct is 3.11
|
| 10 |
+
REQUIRED_ENV_VARS = ["DATABASE_URL", "API_KEY", "SECRET_KEY"]
|
| 11 |
+
WRONG_PORTS = [3000, 5000, 9000]
|
| 12 |
+
WRONG_TEST_COMMANDS = [
|
| 13 |
+
"python -m pytest tests/ --collect-only", # collects but never runs
|
| 14 |
+
"python -m unittest discover tests/", # wrong runner
|
| 15 |
+
"python -m pytest tests/ --dry-run", # dry-run, no output
|
| 16 |
+
]
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def generate_easy_scenario(seed: int) -> dict:
|
| 20 |
+
"""
|
| 21 |
+
Returns a filesystem dict + answer_key.
|
| 22 |
+
The filesystem has requirements.txt missing one required package.
|
| 23 |
+
"""
|
| 24 |
+
rng = random.Random(seed)
|
| 25 |
+
all_packages = get_packages("easy")
|
| 26 |
+
missing = rng.choice(all_packages)
|
| 27 |
+
present = [p for p in all_packages if p != missing]
|
| 28 |
+
|
| 29 |
+
return {
|
| 30 |
+
"filesystem": {
|
| 31 |
+
"requirements.txt": "\n".join(present) + "\n",
|
| 32 |
+
"pipeline.yaml": "stages:\n - install\n",
|
| 33 |
+
"logs/install.log": "",
|
| 34 |
+
"app.py": "import flask\nimport numpy\n# app code here\n",
|
| 35 |
+
},
|
| 36 |
+
"answer_key": {
|
| 37 |
+
"fixes": {
|
| 38 |
+
"requirements.txt": missing,
|
| 39 |
+
},
|
| 40 |
+
},
|
| 41 |
+
}
|
| 42 |
+
|
| 43 |
+
def _medium_type_a(rng: random.Random, all_packages: list) -> dict:
|
| 44 |
+
"""
|
| 45 |
+
Type A: wrong Python version (Dockerfile) + missing env var (.env.ci).
|
| 46 |
+
Pipeline: install → env_check → docker_build
|
| 47 |
+
Both files must be fixed. install always passes.
|
| 48 |
+
"""
|
| 49 |
+
wrong_version = rng.choice(PYTHON_VERSIONS)
|
| 50 |
+
missing_var = rng.choice(REQUIRED_ENV_VARS)
|
| 51 |
+
present_vars = {v: "placeholder" for v in REQUIRED_ENV_VARS if v != missing_var}
|
| 52 |
+
env_ci_content = "".join(f"{k}={v}\n" for k, v in sorted(present_vars.items()))
|
| 53 |
+
|
| 54 |
+
return {
|
| 55 |
+
"filesystem": {
|
| 56 |
+
"requirements.txt": "\n".join(all_packages) + "\n",
|
| 57 |
+
"Dockerfile": (
|
| 58 |
+
f"FROM python:{wrong_version}-slim\n"
|
| 59 |
+
"WORKDIR /app\n"
|
| 60 |
+
"COPY requirements.txt .\n"
|
| 61 |
+
"RUN pip install -r requirements.txt\n"
|
| 62 |
+
"COPY . .\n"
|
| 63 |
+
'CMD ["python", "app.py"]\n'
|
| 64 |
+
),
|
| 65 |
+
".env.ci": env_ci_content,
|
| 66 |
+
"pipeline.yaml": "stages:\n - install\n - env_check\n - docker_build\n",
|
| 67 |
+
"app.py": "import flask\n# app code here\n",
|
| 68 |
+
"logs/install.log": "",
|
| 69 |
+
},
|
| 70 |
+
"answer_key": {
|
| 71 |
+
"fixes": {
|
| 72 |
+
"Dockerfile": "python:3.11",
|
| 73 |
+
".env.ci": missing_var,
|
| 74 |
+
},
|
| 75 |
+
},
|
| 76 |
+
}
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def _medium_type_b(rng: random.Random, all_packages: list) -> dict:
|
| 80 |
+
"""
|
| 81 |
+
Type B: missing package (requirements.txt) + deployment flag off (deploy_config.yml).
|
| 82 |
+
Pipeline: install → config_validate → smoke_test
|
| 83 |
+
install fails first; after fixing, config_validate fails.
|
| 84 |
+
"""
|
| 85 |
+
missing_pkg = rng.choice(all_packages)
|
| 86 |
+
present_pkgs = [p for p in all_packages if p != missing_pkg]
|
| 87 |
+
|
| 88 |
+
return {
|
| 89 |
+
"filesystem": {
|
| 90 |
+
"requirements.txt": "\n".join(present_pkgs) + "\n",
|
| 91 |
+
"deploy_config.yml": (
|
| 92 |
+
"target_env: production\n"
|
| 93 |
+
"deploy_enabled: false\n" # BUG: must be true
|
| 94 |
+
"replicas: 2\n"
|
| 95 |
+
"health_check_path: /health\n"
|
| 96 |
+
"timeout: 30\n"
|
| 97 |
+
),
|
| 98 |
+
"pipeline.yaml": "stages:\n - install\n - config_validate\n - smoke_test\n",
|
| 99 |
+
"app.py": "import flask\n# app code here\n",
|
| 100 |
+
"logs/install.log": "",
|
| 101 |
+
},
|
| 102 |
+
"answer_key": {
|
| 103 |
+
"fixes": {
|
| 104 |
+
"requirements.txt": missing_pkg,
|
| 105 |
+
"deploy_config.yml": "deploy_enabled: true",
|
| 106 |
+
},
|
| 107 |
+
},
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def _medium_type_c(rng: random.Random, all_packages: list) -> dict:
|
| 112 |
+
"""
|
| 113 |
+
Type C: wrong test command (Makefile) + missing env var (.env.ci).
|
| 114 |
+
Pipeline: install → env_check → test
|
| 115 |
+
env_check fails first; after fixing, test fails due to bad Makefile.
|
| 116 |
+
"""
|
| 117 |
+
wrong_cmd = rng.choice(WRONG_TEST_COMMANDS)
|
| 118 |
+
missing_var = rng.choice(REQUIRED_ENV_VARS)
|
| 119 |
+
present_vars = {v: "placeholder" for v in REQUIRED_ENV_VARS if v != missing_var}
|
| 120 |
+
env_ci_content = "".join(f"{k}={v}\n" for k, v in sorted(present_vars.items()))
|
| 121 |
+
|
| 122 |
+
return {
|
| 123 |
+
"filesystem": {
|
| 124 |
+
"requirements.txt": "\n".join(all_packages) + "\n",
|
| 125 |
+
".env.ci": env_ci_content,
|
| 126 |
+
"Makefile": (
|
| 127 |
+
".PHONY: test\n"
|
| 128 |
+
"test:\n"
|
| 129 |
+
f"\t{wrong_cmd}\n"
|
| 130 |
+
),
|
| 131 |
+
"pipeline.yaml": "stages:\n - install\n - env_check\n - test\n",
|
| 132 |
+
"app.py": "import flask\n# app code here\n",
|
| 133 |
+
"logs/install.log": "",
|
| 134 |
+
},
|
| 135 |
+
"answer_key": {
|
| 136 |
+
"fixes": {
|
| 137 |
+
".env.ci": missing_var,
|
| 138 |
+
"Makefile": "python -m pytest tests/",
|
| 139 |
+
},
|
| 140 |
+
},
|
| 141 |
+
}
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
def _medium_type_d(rng: random.Random, all_packages: list) -> dict:
|
| 145 |
+
"""
|
| 146 |
+
Type D: wrong port (service.yaml) + wrong Python version (Dockerfile).
|
| 147 |
+
Pipeline: install → port_check → docker_build
|
| 148 |
+
port_check fails first; after fixing, docker_build fails.
|
| 149 |
+
install always passes.
|
| 150 |
+
"""
|
| 151 |
+
wrong_version = rng.choice(PYTHON_VERSIONS)
|
| 152 |
+
wrong_port = rng.choice(WRONG_PORTS)
|
| 153 |
+
|
| 154 |
+
return {
|
| 155 |
+
"filesystem": {
|
| 156 |
+
"requirements.txt": "\n".join(all_packages) + "\n",
|
| 157 |
+
"Dockerfile": (
|
| 158 |
+
f"FROM python:{wrong_version}-slim\n"
|
| 159 |
+
"WORKDIR /app\n"
|
| 160 |
+
"COPY requirements.txt .\n"
|
| 161 |
+
"RUN pip install -r requirements.txt\n"
|
| 162 |
+
"COPY . .\n"
|
| 163 |
+
'CMD ["python", "app.py"]\n'
|
| 164 |
+
),
|
| 165 |
+
"service.yaml": (
|
| 166 |
+
"apiVersion: v1\n"
|
| 167 |
+
"kind: Service\n"
|
| 168 |
+
"metadata:\n"
|
| 169 |
+
" name: app\n"
|
| 170 |
+
"spec:\n"
|
| 171 |
+
f" port: {wrong_port}\n"
|
| 172 |
+
),
|
| 173 |
+
"pipeline.yaml": "stages:\n - install\n - port_check\n - docker_build\n",
|
| 174 |
+
"app.py": "import flask\n# app code here\n",
|
| 175 |
+
"logs/install.log": "",
|
| 176 |
+
},
|
| 177 |
+
"answer_key": {
|
| 178 |
+
"fixes": {
|
| 179 |
+
"service.yaml": "port: 8080",
|
| 180 |
+
"Dockerfile": "python:3.11",
|
| 181 |
+
},
|
| 182 |
+
},
|
| 183 |
+
}
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
def generate_medium_scenario(seed: int) -> dict:
|
| 187 |
+
"""
|
| 188 |
+
Randomly selects one of four structurally distinct medium scenario types,
|
| 189 |
+
then generates the specifics (which var, which version, etc.) from the
|
| 190 |
+
same seed. Same seed → same scenario every time.
|
| 191 |
+
"""
|
| 192 |
+
rng = random.Random(seed)
|
| 193 |
+
all_packages = get_packages("medium")
|
| 194 |
+
scenario_type = rng.choice(["A", "B", "C", "D"])
|
| 195 |
+
|
| 196 |
+
if scenario_type == "A":
|
| 197 |
+
return _medium_type_a(rng, all_packages)
|
| 198 |
+
elif scenario_type == "B":
|
| 199 |
+
return _medium_type_b(rng, all_packages)
|
| 200 |
+
elif scenario_type == "C":
|
| 201 |
+
return _medium_type_c(rng, all_packages)
|
| 202 |
+
else:
|
| 203 |
+
return _medium_type_d(rng, all_packages)
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
def _hard_type_a(rng: random.Random, all_packages: list) -> dict:
|
| 207 |
+
"""
|
| 208 |
+
Type A: ci.yml ordering → Dockerfile alpine → numpy version pin.
|
| 209 |
+
Pipeline: ci_validate → docker_build(strict) → install(hard).
|
| 210 |
+
"""
|
| 211 |
+
_ = rng # reserved for future per-seed variation
|
| 212 |
+
requirements_lines = [
|
| 213 |
+
"numpy==1.21" if pkg == "numpy" else pkg
|
| 214 |
+
for pkg in all_packages
|
| 215 |
+
]
|
| 216 |
+
|
| 217 |
+
return {
|
| 218 |
+
"filesystem": {
|
| 219 |
+
"requirements.txt": "\n".join(requirements_lines) + "\n",
|
| 220 |
+
"Dockerfile": (
|
| 221 |
+
"FROM python:3.11-alpine\n"
|
| 222 |
+
"WORKDIR /app\n"
|
| 223 |
+
"COPY requirements.txt .\n"
|
| 224 |
+
"RUN pip install -r requirements.txt\n"
|
| 225 |
+
"COPY . .\n"
|
| 226 |
+
'CMD ["python", "app.py"]\n'
|
| 227 |
+
),
|
| 228 |
+
"ci.yml": "stages: test, build, install\n",
|
| 229 |
+
"pipeline.yaml": "stages:\n - ci_validate\n - docker_build\n - install\n",
|
| 230 |
+
"app.py": "import flask\nimport numpy\n# app code here\n",
|
| 231 |
+
"logs/install.log": "",
|
| 232 |
+
},
|
| 233 |
+
"answer_key": {
|
| 234 |
+
"fixes": {
|
| 235 |
+
"ci.yml": "install, build, test",
|
| 236 |
+
"Dockerfile": "python:3.11-slim",
|
| 237 |
+
"requirements.txt": "numpy==1.26",
|
| 238 |
+
},
|
| 239 |
+
},
|
| 240 |
+
}
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
def _hard_type_b(rng: random.Random, all_packages: list) -> dict:
|
| 244 |
+
"""
|
| 245 |
+
Type B: ci.yml ordering → missing env var → wrong test command.
|
| 246 |
+
Pipeline: ci_validate → env_check → test.
|
| 247 |
+
requirements.txt is clean; no Dockerfile needed.
|
| 248 |
+
"""
|
| 249 |
+
wrong_cmd = rng.choice(WRONG_TEST_COMMANDS)
|
| 250 |
+
missing_var = rng.choice(REQUIRED_ENV_VARS)
|
| 251 |
+
present_vars = {v: "placeholder" for v in REQUIRED_ENV_VARS if v != missing_var}
|
| 252 |
+
env_ci_content = "".join(f"{k}={v}\n" for k, v in sorted(present_vars.items()))
|
| 253 |
+
|
| 254 |
+
return {
|
| 255 |
+
"filesystem": {
|
| 256 |
+
"requirements.txt": "\n".join(all_packages) + "\n",
|
| 257 |
+
"ci.yml": "stages: test, build, install\n",
|
| 258 |
+
".env.ci": env_ci_content,
|
| 259 |
+
"Makefile": (
|
| 260 |
+
".PHONY: test\n"
|
| 261 |
+
"test:\n"
|
| 262 |
+
f"\t{wrong_cmd}\n"
|
| 263 |
+
),
|
| 264 |
+
"pipeline.yaml": "stages:\n - ci_validate\n - env_check\n - test\n",
|
| 265 |
+
"app.py": "import flask\n# app code here\n",
|
| 266 |
+
"logs/install.log": "",
|
| 267 |
+
},
|
| 268 |
+
"answer_key": {
|
| 269 |
+
"fixes": {
|
| 270 |
+
"ci.yml": "install, build, test",
|
| 271 |
+
".env.ci": missing_var,
|
| 272 |
+
"Makefile": "python -m pytest tests/",
|
| 273 |
+
},
|
| 274 |
+
},
|
| 275 |
+
}
|
| 276 |
+
|
| 277 |
+
|
| 278 |
+
def _hard_type_c(rng: random.Random, all_packages: list) -> dict:
|
| 279 |
+
"""
|
| 280 |
+
Type C: Dockerfile alpine → deploy disabled → wrong service port.
|
| 281 |
+
Pipeline: docker_build(strict) → config_validate → port_check.
|
| 282 |
+
"""
|
| 283 |
+
_ = rng # reserved for future per-seed variation
|
| 284 |
+
wrong_port = rng.choice(WRONG_PORTS)
|
| 285 |
+
|
| 286 |
+
return {
|
| 287 |
+
"filesystem": {
|
| 288 |
+
"requirements.txt": "\n".join(all_packages) + "\n",
|
| 289 |
+
"Dockerfile": (
|
| 290 |
+
"FROM python:3.11-alpine\n"
|
| 291 |
+
"WORKDIR /app\n"
|
| 292 |
+
"COPY requirements.txt .\n"
|
| 293 |
+
"RUN pip install -r requirements.txt\n"
|
| 294 |
+
"COPY . .\n"
|
| 295 |
+
'CMD ["python", "app.py"]\n'
|
| 296 |
+
),
|
| 297 |
+
"deploy_config.yml": (
|
| 298 |
+
"target_env: production\n"
|
| 299 |
+
"deploy_enabled: false\n"
|
| 300 |
+
"replicas: 2\n"
|
| 301 |
+
"health_check_path: /health\n"
|
| 302 |
+
"timeout: 30\n"
|
| 303 |
+
),
|
| 304 |
+
"service.yaml": (
|
| 305 |
+
"apiVersion: v1\n"
|
| 306 |
+
"kind: Service\n"
|
| 307 |
+
"metadata:\n"
|
| 308 |
+
" name: app\n"
|
| 309 |
+
"spec:\n"
|
| 310 |
+
f" port: {wrong_port}\n"
|
| 311 |
+
),
|
| 312 |
+
"pipeline.yaml": "stages:\n - docker_build\n - config_validate\n - port_check\n",
|
| 313 |
+
"app.py": "import flask\n# app code here\n",
|
| 314 |
+
"logs/install.log": "",
|
| 315 |
+
},
|
| 316 |
+
"answer_key": {
|
| 317 |
+
"fixes": {
|
| 318 |
+
"Dockerfile": "python:3.11-slim",
|
| 319 |
+
"deploy_config.yml": "deploy_enabled: true",
|
| 320 |
+
"service.yaml": "port: 8080",
|
| 321 |
+
},
|
| 322 |
+
},
|
| 323 |
+
}
|
| 324 |
+
|
| 325 |
+
|
| 326 |
+
def _hard_type_d(rng: random.Random, all_packages: list) -> dict:
|
| 327 |
+
"""
|
| 328 |
+
Type D: missing package → missing env var → Dockerfile alpine.
|
| 329 |
+
Pipeline: install(hard) → env_check → docker_build(strict).
|
| 330 |
+
"""
|
| 331 |
+
missing_pkg = rng.choice(all_packages)
|
| 332 |
+
present_pkgs = [p for p in all_packages if p != missing_pkg]
|
| 333 |
+
missing_var = rng.choice(REQUIRED_ENV_VARS)
|
| 334 |
+
present_vars = {v: "placeholder" for v in REQUIRED_ENV_VARS if v != missing_var}
|
| 335 |
+
env_ci_content = "".join(f"{k}={v}\n" for k, v in sorted(present_vars.items()))
|
| 336 |
+
|
| 337 |
+
return {
|
| 338 |
+
"filesystem": {
|
| 339 |
+
"requirements.txt": "\n".join(present_pkgs) + "\n",
|
| 340 |
+
".env.ci": env_ci_content,
|
| 341 |
+
"Dockerfile": (
|
| 342 |
+
"FROM python:3.11-alpine\n"
|
| 343 |
+
"WORKDIR /app\n"
|
| 344 |
+
"COPY requirements.txt .\n"
|
| 345 |
+
"RUN pip install -r requirements.txt\n"
|
| 346 |
+
"COPY . .\n"
|
| 347 |
+
'CMD ["python", "app.py"]\n'
|
| 348 |
+
),
|
| 349 |
+
"pipeline.yaml": "stages:\n - install\n - env_check\n - docker_build\n",
|
| 350 |
+
"app.py": "import flask\n# app code here\n",
|
| 351 |
+
"logs/install.log": "",
|
| 352 |
+
},
|
| 353 |
+
"answer_key": {
|
| 354 |
+
"fixes": {
|
| 355 |
+
"requirements.txt": missing_pkg,
|
| 356 |
+
".env.ci": missing_var,
|
| 357 |
+
"Dockerfile": "python:3.11-slim",
|
| 358 |
+
},
|
| 359 |
+
},
|
| 360 |
+
}
|
| 361 |
+
|
| 362 |
+
|
| 363 |
+
def generate_hard_scenario(seed: int) -> dict:
|
| 364 |
+
"""
|
| 365 |
+
Randomly selects one of four structurally distinct hard scenario types,
|
| 366 |
+
then generates the specifics from the same seed. Each variant is a
|
| 367 |
+
three-fix cascading failure — each pipeline run stops at the first
|
| 368 |
+
failing stage, so bugs surface one at a time as the agent fixes them.
|
| 369 |
+
Same seed → same scenario every time.
|
| 370 |
+
"""
|
| 371 |
+
rng = random.Random(seed)
|
| 372 |
+
all_packages = get_packages("hard")
|
| 373 |
+
scenario_type = rng.choice(["A", "B", "C", "D"])
|
| 374 |
+
|
| 375 |
+
if scenario_type == "A":
|
| 376 |
+
return _hard_type_a(rng, all_packages)
|
| 377 |
+
elif scenario_type == "B":
|
| 378 |
+
return _hard_type_b(rng, all_packages)
|
| 379 |
+
elif scenario_type == "C":
|
| 380 |
+
return _hard_type_c(rng, all_packages)
|
| 381 |
+
else:
|
| 382 |
+
return _hard_type_d(rng, all_packages)
|
environment/grader.py
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Grader for the CI/CD Doctor environment.
|
| 3 |
+
|
| 4 |
+
Reward shape:
|
| 5 |
+
fixes_applied_fraction * 0.35 proportional credit for each answer_key fix
|
| 6 |
+
that is present in the filesystem (emitted
|
| 7 |
+
incrementally as each fix lands, not all-or-
|
| 8 |
+
nothing — on a 2-fix task, each fix is worth
|
| 9 |
+
+0.175)
|
| 10 |
+
pipeline_passed +0.50 pipeline_status == "passed" (terminal)
|
| 11 |
+
|
| 12 |
+
Total positive: 0.85 from grade() + shaped bonuses from balance_score().
|
| 13 |
+
|
| 14 |
+
Investigation milestones (investigated, logs_read, correct_file_located) are
|
| 15 |
+
still tracked in state.milestones for the balance_score() logic but give no
|
| 16 |
+
reward — reading a file is not progress, fixing it is.
|
| 17 |
+
|
| 18 |
+
balance_score() applies per-step shaped adjustments on top of the tier delta:
|
| 19 |
+
+0.05 First read of each answer-key file (exploration bonus, max 2 files)
|
| 20 |
+
-0.05 cat on a file already read this episode (redundant read penalty)
|
| 21 |
+
-0.10 pipeline run with no filesystem changes since last run (idle run)
|
| 22 |
+
-0.01 * overage each step taken beyond the task's ideal step count
|
| 23 |
+
(efficiency penalty scales linearly with how far past ideal — at
|
| 24 |
+
ideal+1 it's -0.01, at ideal+5 it's -0.05; cumulative cost on a
|
| 25 |
+
9-step overage tops out around -0.45)
|
| 26 |
+
-0.08 agent has read the correct file but runs pipeline again with no edit
|
| 27 |
+
(exploitation trap — knows the problem, not acting on it)
|
| 28 |
+
"""
|
| 29 |
+
|
| 30 |
+
from dataclasses import dataclass, field
|
| 31 |
+
|
| 32 |
+
from models import PipelineState
|
| 33 |
+
|
| 34 |
+
CORRECT_FILE_EDITED_TOTAL = 0.2
|
| 35 |
+
|
| 36 |
+
TIER_REWARDS: dict[str, float] = {
|
| 37 |
+
"investigated": 0.0,
|
| 38 |
+
"logs_read": 0.0,
|
| 39 |
+
"correct_file_located": 0.01,
|
| 40 |
+
"pipeline_passed": 0.50,
|
| 41 |
+
"optimal_step":0.05
|
| 42 |
+
}
|
| 43 |
+
|
| 44 |
+
PENALTIES: dict[str, float] = {
|
| 45 |
+
"idle_pipeline_run": -0.10,
|
| 46 |
+
"redundant_read": -0.05,
|
| 47 |
+
"over_ideal_step": -0.01,
|
| 48 |
+
"exploitation_trap": -0.08,
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
@dataclass
|
| 52 |
+
class StepContext:
|
| 53 |
+
cmd_type: str
|
| 54 |
+
filename: str | None = None
|
| 55 |
+
files_read: set[str] = field(default_factory=set)
|
| 56 |
+
fs_changed_since_last_run: bool = True
|
| 57 |
+
step_count: int = 0
|
| 58 |
+
max_steps: int = 15
|
| 59 |
+
ideal_steps: int = 6
|
| 60 |
+
pipeline_runs_since_last_edit: int = 0
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def _fixes_applied_fraction(state: PipelineState) -> float:
|
| 64 |
+
"""
|
| 65 |
+
Fraction of answer_key fixes that are currently present in the filesystem.
|
| 66 |
+
Returns a value in [0.0, 1.0]. Each fix contributes incrementally the
|
| 67 |
+
moment its fragment appears in the target file, so a 2-fix task rewards
|
| 68 |
+
each correct edit as it happens rather than only when both are done.
|
| 69 |
+
"""
|
| 70 |
+
fixes = state.answer_key.get("fixes", {})
|
| 71 |
+
if not fixes:
|
| 72 |
+
return 0.0
|
| 73 |
+
applied = sum(
|
| 74 |
+
1 for filename, fragment in fixes.items()
|
| 75 |
+
if fragment in state.filesystem.get(filename, "")
|
| 76 |
+
)
|
| 77 |
+
return applied / len(fixes)
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def grade(state: PipelineState) -> float:
|
| 81 |
+
"""
|
| 82 |
+
Compute the total earned grade from state. Fractional credit for fixes
|
| 83 |
+
in the filesystem, plus the terminal bonus on pipeline pass. Investigation
|
| 84 |
+
milestones contribute 0 — reading a file is not progress, fixing it is.
|
| 85 |
+
"""
|
| 86 |
+
score = CORRECT_FILE_EDITED_TOTAL * _fixes_applied_fraction(state)
|
| 87 |
+
|
| 88 |
+
unlocked = set(state.milestones)
|
| 89 |
+
if state.pipeline_status == "passed":
|
| 90 |
+
unlocked.add("pipeline_passed")
|
| 91 |
+
score += sum(TIER_REWARDS[tier] for tier in unlocked if tier in TIER_REWARDS)
|
| 92 |
+
|
| 93 |
+
return round(score, 2)
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
def balance_score(state: PipelineState, ctx: StepContext) -> float:
|
| 97 |
+
"""
|
| 98 |
+
Per-step shaped reward adjustment on top of the raw grade delta.
|
| 99 |
+
|
| 100 |
+
Returns a float (may be negative). The caller adds this to the grade
|
| 101 |
+
delta to produce the final step reward.
|
| 102 |
+
|
| 103 |
+
The two goals:
|
| 104 |
+
- Encourage exploration: small bonus the first time the agent reads a
|
| 105 |
+
file that needs fixing (up to 2 files per episode).
|
| 106 |
+
- Discourage waste: penalties for re-reading, idle pipeline runs,
|
| 107 |
+
burning the step budget, and knowing the fix but not applying it.
|
| 108 |
+
"""
|
| 109 |
+
adjustment = 0.0
|
| 110 |
+
fix_files = set(state.answer_key.get("fixes", {}).keys())
|
| 111 |
+
|
| 112 |
+
if ctx.cmd_type == "cat" and ctx.filename:
|
| 113 |
+
if ctx.filename in fix_files and ctx.filename not in ctx.files_read:
|
| 114 |
+
# First read of a file that needs fixing — exploration bonus.
|
| 115 |
+
# Cap at 2 files total to avoid rewarding excessive exploration.
|
| 116 |
+
already_explored = sum(1 for f in ctx.files_read if f in fix_files)
|
| 117 |
+
if already_explored < 2:
|
| 118 |
+
adjustment += 0.05
|
| 119 |
+
elif ctx.filename in ctx.files_read:
|
| 120 |
+
# Already read this file — wasted step.
|
| 121 |
+
adjustment += PENALTIES["redundant_read"]
|
| 122 |
+
|
| 123 |
+
if ctx.cmd_type == "pipeline_run":
|
| 124 |
+
if not ctx.fs_changed_since_last_run:
|
| 125 |
+
# Nothing changed since the last run — this reveals no new info.
|
| 126 |
+
adjustment += PENALTIES["idle_pipeline_run"]
|
| 127 |
+
|
| 128 |
+
if (
|
| 129 |
+
"correct_file_located" in state.milestones
|
| 130 |
+
and ctx.pipeline_runs_since_last_edit >= 1
|
| 131 |
+
):
|
| 132 |
+
# Agent has already read the right file and run the pipeline at
|
| 133 |
+
# least once since its last edit — it knows what to fix but is
|
| 134 |
+
# stalling instead of applying the fix.
|
| 135 |
+
adjustment += PENALTIES["exploitation_trap"]
|
| 136 |
+
|
| 137 |
+
if ctx.step_count > ctx.ideal_steps:
|
| 138 |
+
overage = ctx.step_count - ctx.ideal_steps
|
| 139 |
+
adjustment += PENALTIES["over_ideal_step"] * overage
|
| 140 |
+
else:
|
| 141 |
+
adjustment += TIER_REWARDS["optimal_step"]
|
| 142 |
+
|
| 143 |
+
return round(adjustment, 2)
|
environment/packages.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Single source of truth for required packages across all task difficulties.
|
| 3 |
+
|
| 4 |
+
Add new entries here when implementing medium and hard scenarios.
|
| 5 |
+
"""
|
| 6 |
+
|
| 7 |
+
PACKAGES: dict[str, list[str]] = {
|
| 8 |
+
"easy": ["flask", "numpy", "pandas", "requests", "pydantic"],
|
| 9 |
+
"medium": ["flask", "numpy", "pandas", "requests", "pydantic"], # install always passes; failures are in Dockerfile + .env.ci
|
| 10 |
+
"hard": ["flask", "numpy", "pandas", "requests", "pydantic"], # install failure is from a numpy version conflict, not a missing package
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def get_packages(task: str) -> list[str]:
|
| 15 |
+
"""Return the required package list for a given task difficulty."""
|
| 16 |
+
if task not in PACKAGES:
|
| 17 |
+
raise ValueError(f"Unknown task: {task!r}. Valid tasks: {list(PACKAGES)}")
|
| 18 |
+
return PACKAGES[task]
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def get_packages_set(task: str) -> set[str]:
|
| 22 |
+
"""Return required packages as a set (used for O(1) lookup in stage_runner)."""
|
| 23 |
+
return set(get_packages(task))
|
environment/parser.py
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Free-form command parser for the CI/CD Doctor environment.
|
| 3 |
+
Converts raw command strings into structured ParsedCommand objects.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import re
|
| 7 |
+
from dataclasses import dataclass
|
| 8 |
+
from typing import Optional
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
@dataclass
|
| 12 |
+
class ParsedCommand:
|
| 13 |
+
type: str # "cat" | "echo_append" | "sed" | "pipeline_run" | "pipeline_logs" | "pipeline_status" | "unknown"
|
| 14 |
+
filename: Optional[str] = None
|
| 15 |
+
content: Optional[str] = None # for echo >>
|
| 16 |
+
pattern: Optional[str] = None # for sed: old value
|
| 17 |
+
replacement: Optional[str] = None # for sed: new value
|
| 18 |
+
stage: Optional[str] = None # for pipeline logs: which stage
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def parse_command(command: str) -> ParsedCommand:
|
| 22 |
+
command = command.strip()
|
| 23 |
+
|
| 24 |
+
# cat <filename>
|
| 25 |
+
if command.startswith("cat "):
|
| 26 |
+
return ParsedCommand(type="cat", filename=command[4:].strip())
|
| 27 |
+
|
| 28 |
+
# echo "text" >> filename
|
| 29 |
+
m = re.match(r'echo\s+"([^"]+)"\s*>>\s*(\S+)', command)
|
| 30 |
+
if m:
|
| 31 |
+
return ParsedCommand(type="echo_append", content=m.group(1), filename=m.group(2))
|
| 32 |
+
|
| 33 |
+
# sed -i 's/old/new/' filename
|
| 34 |
+
m = re.match(r"sed\s+-i\s+'s/([^/]+)/([^/]*)/'[\s]+(\S+)", command)
|
| 35 |
+
if m:
|
| 36 |
+
return ParsedCommand(type="sed", pattern=m.group(1), replacement=m.group(2), filename=m.group(3))
|
| 37 |
+
|
| 38 |
+
# pipeline run
|
| 39 |
+
if command == "pipeline run":
|
| 40 |
+
return ParsedCommand(type="pipeline_run")
|
| 41 |
+
|
| 42 |
+
# pipeline logs [stage]
|
| 43 |
+
m = re.match(r"pipeline\s+logs(?:\s+(\S+))?\s*$", command)
|
| 44 |
+
if m:
|
| 45 |
+
return ParsedCommand(type="pipeline_logs", stage=m.group(1))
|
| 46 |
+
|
| 47 |
+
# pipeline status
|
| 48 |
+
if command == "pipeline status":
|
| 49 |
+
return ParsedCommand(type="pipeline_status")
|
| 50 |
+
|
| 51 |
+
return ParsedCommand(type="unknown")
|
environment/stage_runner.py
ADDED
|
@@ -0,0 +1,414 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Simulated pipeline stage runner for the CI/CD Doctor environment.
|
| 3 |
+
No real pip or subprocess — pure deterministic logic.
|
| 4 |
+
|
| 5 |
+
Error message design principle: report WHICH stage failed and WHICH file to
|
| 6 |
+
inspect, but NOT the exact value that is wrong. The agent must read the file
|
| 7 |
+
and reason about the fix itself.
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
import re
|
| 11 |
+
|
| 12 |
+
from .packages import get_packages_set
|
| 13 |
+
|
| 14 |
+
CORRECT_PYTHON_VERSION = "3.11"
|
| 15 |
+
REQUIRED_ENV_VARS = {"DATABASE_URL", "API_KEY", "SECRET_KEY"}
|
| 16 |
+
|
| 17 |
+
def run_install_stage(filesystem: dict, task: str = "easy") -> dict:
|
| 18 |
+
"""
|
| 19 |
+
Reads requirements.txt. Fails if any required package is missing, or
|
| 20 |
+
(hard task) if numpy is pinned to an incompatible version.
|
| 21 |
+
|
| 22 |
+
Version pins are stripped before comparing against the required set, so
|
| 23 |
+
`numpy==1.21` parses as `numpy` for the missing-package check.
|
| 24 |
+
"""
|
| 25 |
+
required = get_packages_set(task)
|
| 26 |
+
content = filesystem.get("requirements.txt", "")
|
| 27 |
+
installed = {
|
| 28 |
+
re.split(r"[=<>!]", line, 1)[0].strip().lower()
|
| 29 |
+
for line in content.splitlines()
|
| 30 |
+
if line.strip()
|
| 31 |
+
}
|
| 32 |
+
missing = required - installed
|
| 33 |
+
|
| 34 |
+
if missing:
|
| 35 |
+
pkg = sorted(missing)[0]
|
| 36 |
+
logs = "Collecting dependencies...\n"
|
| 37 |
+
logs += f" Collecting {pkg}\n"
|
| 38 |
+
logs += f"ERROR: Could not find a version that satisfies the requirement {pkg}\n"
|
| 39 |
+
logs += f"ERROR: No matching distribution found for {pkg}\n"
|
| 40 |
+
logs += " Add the missing package to requirements.txt.\n"
|
| 41 |
+
return {"exit_code": 1, "logs": logs}
|
| 42 |
+
|
| 43 |
+
if task == "hard" and "numpy==1.21" in content:
|
| 44 |
+
logs = "Collecting dependencies...\n"
|
| 45 |
+
logs += " Collecting numpy==1.21\n"
|
| 46 |
+
logs += "ERROR: ResolutionImpossible: dependency conflict detected.\n"
|
| 47 |
+
logs += " requirements.txt pins numpy==1.21, but a transitive dependency\n"
|
| 48 |
+
logs += " requires numpy>=1.26. Update the numpy pin in requirements.txt.\n"
|
| 49 |
+
return {"exit_code": 1, "logs": logs}
|
| 50 |
+
|
| 51 |
+
logs = "Collecting dependencies...\n"
|
| 52 |
+
logs += "Successfully installed " + " ".join(sorted(required)) + "\n"
|
| 53 |
+
return {"exit_code": 0, "logs": logs}
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def run_env_check_stage(filesystem: dict) -> dict:
|
| 57 |
+
"""
|
| 58 |
+
Checks that .env.ci defines all required environment variables.
|
| 59 |
+
Error reports which variables are required so the agent can compare against the file.
|
| 60 |
+
"""
|
| 61 |
+
content = filesystem.get(".env.ci", "")
|
| 62 |
+
defined = {
|
| 63 |
+
line.split("=")[0].strip()
|
| 64 |
+
for line in content.splitlines()
|
| 65 |
+
if "=" in line and line.strip()
|
| 66 |
+
}
|
| 67 |
+
missing = REQUIRED_ENV_VARS - defined
|
| 68 |
+
|
| 69 |
+
if not missing:
|
| 70 |
+
logs = "Environment check passed.\n"
|
| 71 |
+
logs += "All required variables present: " + ", ".join(sorted(REQUIRED_ENV_VARS)) + "\n"
|
| 72 |
+
return {"exit_code": 0, "logs": logs}
|
| 73 |
+
else:
|
| 74 |
+
logs = "Environment check failed.\n"
|
| 75 |
+
logs += f" Required variables: {', '.join(sorted(REQUIRED_ENV_VARS))}\n"
|
| 76 |
+
logs += " Not all required variables are defined in .env.ci.\n"
|
| 77 |
+
return {"exit_code": 1, "logs": logs}
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def run_docker_build_stage(filesystem: dict, strict_tag: bool = False) -> dict:
|
| 81 |
+
"""
|
| 82 |
+
Checks that Dockerfile uses the correct Python base image.
|
| 83 |
+
|
| 84 |
+
In the default (medium) mode, any `python:3.11` tag is accepted. With
|
| 85 |
+
`strict_tag=True` (hard task), the full tag must be `python:3.11-slim` —
|
| 86 |
+
the alpine variant is rejected because it lacks the glibc-based system
|
| 87 |
+
libraries that the project's native-code dependencies need.
|
| 88 |
+
"""
|
| 89 |
+
content = filesystem.get("Dockerfile", "")
|
| 90 |
+
|
| 91 |
+
if strict_tag:
|
| 92 |
+
if "python:3.11-slim" in content:
|
| 93 |
+
logs = "Step 1/5 : FROM python:3.11-slim\n"
|
| 94 |
+
logs += "Successfully built docker image\n"
|
| 95 |
+
return {"exit_code": 0, "logs": logs}
|
| 96 |
+
logs = "Step 1/5 : FROM ...\n"
|
| 97 |
+
logs += "ERROR: Docker build failed.\n"
|
| 98 |
+
logs += " Base image rejected: the alpine variant lacks the system\n"
|
| 99 |
+
logs += " libraries (glibc, build tools) required by the project's\n"
|
| 100 |
+
logs += " native dependencies.\n"
|
| 101 |
+
logs += " Expected base image: python:3.11-slim\n"
|
| 102 |
+
logs += " Inspect your Dockerfile.\n"
|
| 103 |
+
return {"exit_code": 1, "logs": logs}
|
| 104 |
+
|
| 105 |
+
if f"python:{CORRECT_PYTHON_VERSION}" in content:
|
| 106 |
+
logs = f"Step 1/5 : FROM python:{CORRECT_PYTHON_VERSION}-slim\n"
|
| 107 |
+
logs += "Successfully built docker image\n"
|
| 108 |
+
return {"exit_code": 0, "logs": logs}
|
| 109 |
+
logs = "Step 1/5 : FROM ...\n"
|
| 110 |
+
logs += "ERROR: Docker build failed.\n"
|
| 111 |
+
logs += f" Expected base image: python:{CORRECT_PYTHON_VERSION}-slim\n"
|
| 112 |
+
logs += " Inspect your Dockerfile.\n"
|
| 113 |
+
return {"exit_code": 1, "logs": logs}
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
def run_config_validate_stage(filesystem: dict) -> dict:
|
| 117 |
+
"""
|
| 118 |
+
Checks that deploy_config.yml has deploy_enabled: true.
|
| 119 |
+
Error reports expected field value so the agent can compare against the file.
|
| 120 |
+
"""
|
| 121 |
+
content = filesystem.get("deploy_config.yml", "")
|
| 122 |
+
if "deploy_enabled: true" in content:
|
| 123 |
+
logs = "Deployment configuration validated.\n"
|
| 124 |
+
logs += "All required settings are correct.\n"
|
| 125 |
+
return {"exit_code": 0, "logs": logs}
|
| 126 |
+
else:
|
| 127 |
+
logs = "ERROR: Deployment configuration is invalid.\n"
|
| 128 |
+
logs += " deploy_enabled must be 'true' in deploy_config.yml.\n"
|
| 129 |
+
return {"exit_code": 1, "logs": logs}
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
def run_smoke_test_stage(filesystem: dict) -> dict:
|
| 133 |
+
"""Always passes — reached only when prior stages succeed."""
|
| 134 |
+
return {"exit_code": 0, "logs": "Smoke test passed. Application started successfully.\n"}
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
def run_test_stage(filesystem: dict) -> dict:
|
| 138 |
+
"""
|
| 139 |
+
Checks that the Makefile's test target runs pytest correctly.
|
| 140 |
+
Fails if the command is --collect-only, --dry-run, or uses unittest.
|
| 141 |
+
Error reports expected command so the agent can compare against the file.
|
| 142 |
+
"""
|
| 143 |
+
content = filesystem.get("Makefile", "")
|
| 144 |
+
correct = (
|
| 145 |
+
"python -m pytest tests/" in content
|
| 146 |
+
and "--collect-only" not in content
|
| 147 |
+
and "--dry-run" not in content
|
| 148 |
+
and "unittest" not in content
|
| 149 |
+
)
|
| 150 |
+
if correct:
|
| 151 |
+
logs = "Running test suite...\n"
|
| 152 |
+
logs += "All tests passed.\n"
|
| 153 |
+
return {"exit_code": 0, "logs": logs}
|
| 154 |
+
else:
|
| 155 |
+
logs = "ERROR: Test runner failed.\n"
|
| 156 |
+
logs += " Expected test command: python -m pytest tests/\n"
|
| 157 |
+
logs += " Check the test command in your Makefile.\n"
|
| 158 |
+
return {"exit_code": 1, "logs": logs}
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
def run_port_check_stage(filesystem: dict) -> dict:
|
| 162 |
+
"""
|
| 163 |
+
Checks that service.yaml exposes port 8080.
|
| 164 |
+
Error reports expected port so the agent can compare against the file.
|
| 165 |
+
"""
|
| 166 |
+
content = filesystem.get("service.yaml", "")
|
| 167 |
+
if "port: 8080" in content:
|
| 168 |
+
logs = "Service configuration validated.\n"
|
| 169 |
+
logs += "Port binding is correct.\n"
|
| 170 |
+
return {"exit_code": 0, "logs": logs}
|
| 171 |
+
else:
|
| 172 |
+
logs = "ERROR: Service configuration validation failed.\n"
|
| 173 |
+
logs += " Expected port: 8080. Inspect service.yaml.\n"
|
| 174 |
+
return {"exit_code": 1, "logs": logs}
|
| 175 |
+
|
| 176 |
+
def run_ci_validate_stage(filesystem: dict) -> dict:
|
| 177 |
+
"""
|
| 178 |
+
Checks that ci.yml defines stages in the correct order: install (and
|
| 179 |
+
build, if present) must appear before test.
|
| 180 |
+
|
| 181 |
+
Expected format is a single comma-separated line like:
|
| 182 |
+
stages: install, build, test
|
| 183 |
+
"""
|
| 184 |
+
content = filesystem.get("ci.yml", "")
|
| 185 |
+
stages_line = ""
|
| 186 |
+
for line in content.splitlines():
|
| 187 |
+
stripped = line.strip()
|
| 188 |
+
if stripped.startswith("stages:"):
|
| 189 |
+
stages_line = stripped[len("stages:"):].strip()
|
| 190 |
+
break
|
| 191 |
+
|
| 192 |
+
stages = [s.strip() for s in stages_line.split(",") if s.strip()]
|
| 193 |
+
if "install" not in stages or "test" not in stages:
|
| 194 |
+
return {
|
| 195 |
+
"exit_code": 1,
|
| 196 |
+
"logs": "ERROR: ci.yml must define both 'install' and 'test' stages.\n",
|
| 197 |
+
}
|
| 198 |
+
|
| 199 |
+
install_idx = stages.index("install")
|
| 200 |
+
test_idx = stages.index("test")
|
| 201 |
+
build_idx = stages.index("build") if "build" in stages else -1
|
| 202 |
+
|
| 203 |
+
if install_idx < test_idx and (build_idx == -1 or build_idx < test_idx):
|
| 204 |
+
return {"exit_code": 0, "logs": "ci.yml validation passed. Stage order is correct.\n"}
|
| 205 |
+
|
| 206 |
+
logs = "ERROR: ci.yml stage ordering is invalid.\n"
|
| 207 |
+
logs += " 'test' stage runs before 'install'/'build' — dependencies won't be\n"
|
| 208 |
+
logs += " installed yet when the tests try to run.\n"
|
| 209 |
+
logs += " Fix: ensure 'install' and 'build' appear before 'test' in the stages list.\n"
|
| 210 |
+
return {"exit_code": 1, "logs": logs}
|
| 211 |
+
|
| 212 |
+
def _run_medium_type_a_pipeline(filesystem: dict) -> dict:
|
| 213 |
+
"""install (always passes) → env_check → docker_build"""
|
| 214 |
+
combined_logs = ""
|
| 215 |
+
|
| 216 |
+
install = run_install_stage(filesystem, task="medium")
|
| 217 |
+
combined_logs += "=== Stage: install ===\n" + install["logs"]
|
| 218 |
+
if install["exit_code"] != 0:
|
| 219 |
+
return {"exit_code": 1, "logs": combined_logs}
|
| 220 |
+
|
| 221 |
+
env = run_env_check_stage(filesystem)
|
| 222 |
+
combined_logs += "=== Stage: env_check ===\n" + env["logs"]
|
| 223 |
+
if env["exit_code"] != 0:
|
| 224 |
+
return {"exit_code": 1, "logs": combined_logs}
|
| 225 |
+
|
| 226 |
+
docker = run_docker_build_stage(filesystem)
|
| 227 |
+
combined_logs += "=== Stage: docker_build ===\n" + docker["logs"]
|
| 228 |
+
return {"exit_code": docker["exit_code"], "logs": combined_logs}
|
| 229 |
+
|
| 230 |
+
|
| 231 |
+
def _run_medium_type_b_pipeline(filesystem: dict) -> dict:
|
| 232 |
+
"""install (missing pkg) → config_validate → smoke_test"""
|
| 233 |
+
combined_logs = ""
|
| 234 |
+
|
| 235 |
+
install = run_install_stage(filesystem, task="medium")
|
| 236 |
+
combined_logs += "=== Stage: install ===\n" + install["logs"]
|
| 237 |
+
if install["exit_code"] != 0:
|
| 238 |
+
return {"exit_code": 1, "logs": combined_logs}
|
| 239 |
+
|
| 240 |
+
config = run_config_validate_stage(filesystem)
|
| 241 |
+
combined_logs += "=== Stage: config_validate ===\n" + config["logs"]
|
| 242 |
+
if config["exit_code"] != 0:
|
| 243 |
+
return {"exit_code": 1, "logs": combined_logs}
|
| 244 |
+
|
| 245 |
+
smoke = run_smoke_test_stage(filesystem)
|
| 246 |
+
combined_logs += "=== Stage: smoke_test ===\n" + smoke["logs"]
|
| 247 |
+
return {"exit_code": smoke["exit_code"], "logs": combined_logs}
|
| 248 |
+
|
| 249 |
+
|
| 250 |
+
def _run_medium_type_c_pipeline(filesystem: dict) -> dict:
|
| 251 |
+
"""install (always passes) → env_check → test (Makefile)"""
|
| 252 |
+
combined_logs = ""
|
| 253 |
+
|
| 254 |
+
install = run_install_stage(filesystem, task="medium")
|
| 255 |
+
combined_logs += "=== Stage: install ===\n" + install["logs"]
|
| 256 |
+
if install["exit_code"] != 0:
|
| 257 |
+
return {"exit_code": 1, "logs": combined_logs}
|
| 258 |
+
|
| 259 |
+
env = run_env_check_stage(filesystem)
|
| 260 |
+
combined_logs += "=== Stage: env_check ===\n" + env["logs"]
|
| 261 |
+
if env["exit_code"] != 0:
|
| 262 |
+
return {"exit_code": 1, "logs": combined_logs}
|
| 263 |
+
|
| 264 |
+
test = run_test_stage(filesystem)
|
| 265 |
+
combined_logs += "=== Stage: test ===\n" + test["logs"]
|
| 266 |
+
return {"exit_code": test["exit_code"], "logs": combined_logs}
|
| 267 |
+
|
| 268 |
+
|
| 269 |
+
def _run_medium_type_d_pipeline(filesystem: dict) -> dict:
|
| 270 |
+
"""install (always passes) → port_check → docker_build"""
|
| 271 |
+
combined_logs = ""
|
| 272 |
+
|
| 273 |
+
install = run_install_stage(filesystem, task="medium")
|
| 274 |
+
combined_logs += "=== Stage: install ===\n" + install["logs"]
|
| 275 |
+
if install["exit_code"] != 0:
|
| 276 |
+
return {"exit_code": 1, "logs": combined_logs}
|
| 277 |
+
|
| 278 |
+
port = run_port_check_stage(filesystem)
|
| 279 |
+
combined_logs += "=== Stage: port_check ===\n" + port["logs"]
|
| 280 |
+
if port["exit_code"] != 0:
|
| 281 |
+
return {"exit_code": 1, "logs": combined_logs}
|
| 282 |
+
|
| 283 |
+
docker = run_docker_build_stage(filesystem)
|
| 284 |
+
combined_logs += "=== Stage: docker_build ===\n" + docker["logs"]
|
| 285 |
+
return {"exit_code": docker["exit_code"], "logs": combined_logs}
|
| 286 |
+
|
| 287 |
+
|
| 288 |
+
def _run_medium_pipeline(filesystem: dict) -> dict:
|
| 289 |
+
"""
|
| 290 |
+
Detects scenario type from the filesystem and dispatches to the right runner.
|
| 291 |
+
Each type has a unique distinguishing file:
|
| 292 |
+
Type B → deploy_config.yml
|
| 293 |
+
Type C → Makefile
|
| 294 |
+
Type D → service.yaml
|
| 295 |
+
Type A → fallback (Dockerfile + .env.ci)
|
| 296 |
+
"""
|
| 297 |
+
if "Makefile" in filesystem:
|
| 298 |
+
return _run_medium_type_c_pipeline(filesystem)
|
| 299 |
+
elif "deploy_config.yml" in filesystem:
|
| 300 |
+
return _run_medium_type_b_pipeline(filesystem)
|
| 301 |
+
elif "service.yaml" in filesystem:
|
| 302 |
+
return _run_medium_type_d_pipeline(filesystem)
|
| 303 |
+
else:
|
| 304 |
+
return _run_medium_type_a_pipeline(filesystem)
|
| 305 |
+
|
| 306 |
+
|
| 307 |
+
def _run_hard_type_a_pipeline(filesystem: dict) -> dict:
|
| 308 |
+
"""ci_validate → docker_build(strict) → install(hard)."""
|
| 309 |
+
combined_logs = ""
|
| 310 |
+
|
| 311 |
+
ci = run_ci_validate_stage(filesystem)
|
| 312 |
+
combined_logs += "=== Stage: ci_validate ===\n" + ci["logs"]
|
| 313 |
+
if ci["exit_code"] != 0:
|
| 314 |
+
return {"exit_code": 1, "logs": combined_logs}
|
| 315 |
+
|
| 316 |
+
docker = run_docker_build_stage(filesystem, strict_tag=True)
|
| 317 |
+
combined_logs += "=== Stage: docker_build ===\n" + docker["logs"]
|
| 318 |
+
if docker["exit_code"] != 0:
|
| 319 |
+
return {"exit_code": 1, "logs": combined_logs}
|
| 320 |
+
|
| 321 |
+
install = run_install_stage(filesystem, task="hard")
|
| 322 |
+
combined_logs += "=== Stage: install ===\n" + install["logs"]
|
| 323 |
+
return {"exit_code": install["exit_code"], "logs": combined_logs}
|
| 324 |
+
|
| 325 |
+
|
| 326 |
+
def _run_hard_type_b_pipeline(filesystem: dict) -> dict:
|
| 327 |
+
"""ci_validate → env_check → test (Makefile)."""
|
| 328 |
+
combined_logs = ""
|
| 329 |
+
|
| 330 |
+
ci = run_ci_validate_stage(filesystem)
|
| 331 |
+
combined_logs += "=== Stage: ci_validate ===\n" + ci["logs"]
|
| 332 |
+
if ci["exit_code"] != 0:
|
| 333 |
+
return {"exit_code": 1, "logs": combined_logs}
|
| 334 |
+
|
| 335 |
+
env = run_env_check_stage(filesystem)
|
| 336 |
+
combined_logs += "=== Stage: env_check ===\n" + env["logs"]
|
| 337 |
+
if env["exit_code"] != 0:
|
| 338 |
+
return {"exit_code": 1, "logs": combined_logs}
|
| 339 |
+
|
| 340 |
+
test = run_test_stage(filesystem)
|
| 341 |
+
combined_logs += "=== Stage: test ===\n" + test["logs"]
|
| 342 |
+
return {"exit_code": test["exit_code"], "logs": combined_logs}
|
| 343 |
+
|
| 344 |
+
|
| 345 |
+
def _run_hard_type_c_pipeline(filesystem: dict) -> dict:
|
| 346 |
+
"""docker_build(strict) → config_validate → port_check."""
|
| 347 |
+
combined_logs = ""
|
| 348 |
+
|
| 349 |
+
docker = run_docker_build_stage(filesystem, strict_tag=True)
|
| 350 |
+
combined_logs += "=== Stage: docker_build ===\n" + docker["logs"]
|
| 351 |
+
if docker["exit_code"] != 0:
|
| 352 |
+
return {"exit_code": 1, "logs": combined_logs}
|
| 353 |
+
|
| 354 |
+
config = run_config_validate_stage(filesystem)
|
| 355 |
+
combined_logs += "=== Stage: config_validate ===\n" + config["logs"]
|
| 356 |
+
if config["exit_code"] != 0:
|
| 357 |
+
return {"exit_code": 1, "logs": combined_logs}
|
| 358 |
+
|
| 359 |
+
port = run_port_check_stage(filesystem)
|
| 360 |
+
combined_logs += "=== Stage: port_check ===\n" + port["logs"]
|
| 361 |
+
return {"exit_code": port["exit_code"], "logs": combined_logs}
|
| 362 |
+
|
| 363 |
+
|
| 364 |
+
def _run_hard_type_d_pipeline(filesystem: dict) -> dict:
|
| 365 |
+
"""install(hard, missing pkg) → env_check → docker_build(strict)."""
|
| 366 |
+
combined_logs = ""
|
| 367 |
+
|
| 368 |
+
install = run_install_stage(filesystem, task="hard")
|
| 369 |
+
combined_logs += "=== Stage: install ===\n" + install["logs"]
|
| 370 |
+
if install["exit_code"] != 0:
|
| 371 |
+
return {"exit_code": 1, "logs": combined_logs}
|
| 372 |
+
|
| 373 |
+
env = run_env_check_stage(filesystem)
|
| 374 |
+
combined_logs += "=== Stage: env_check ===\n" + env["logs"]
|
| 375 |
+
if env["exit_code"] != 0:
|
| 376 |
+
return {"exit_code": 1, "logs": combined_logs}
|
| 377 |
+
|
| 378 |
+
docker = run_docker_build_stage(filesystem, strict_tag=True)
|
| 379 |
+
combined_logs += "=== Stage: docker_build ===\n" + docker["logs"]
|
| 380 |
+
return {"exit_code": docker["exit_code"], "logs": combined_logs}
|
| 381 |
+
|
| 382 |
+
|
| 383 |
+
def _run_hard_pipeline(filesystem: dict) -> dict:
|
| 384 |
+
"""
|
| 385 |
+
Detects hard scenario variant from the filesystem and dispatches.
|
| 386 |
+
Cascading failures: each stage is only reached after the previous
|
| 387 |
+
passes, so bugs surface one at a time as the agent fixes them.
|
| 388 |
+
|
| 389 |
+
Distinguishing markers:
|
| 390 |
+
Type B → ci.yml + Makefile
|
| 391 |
+
Type A → ci.yml alone
|
| 392 |
+
Type C → service.yaml (with deploy_config.yml)
|
| 393 |
+
Type D → fallback (Dockerfile + .env.ci, no ci.yml)
|
| 394 |
+
"""
|
| 395 |
+
if "ci.yml" in filesystem and "Makefile" in filesystem:
|
| 396 |
+
return _run_hard_type_b_pipeline(filesystem)
|
| 397 |
+
if "ci.yml" in filesystem:
|
| 398 |
+
return _run_hard_type_a_pipeline(filesystem)
|
| 399 |
+
if "service.yaml" in filesystem:
|
| 400 |
+
return _run_hard_type_c_pipeline(filesystem)
|
| 401 |
+
return _run_hard_type_d_pipeline(filesystem)
|
| 402 |
+
|
| 403 |
+
|
| 404 |
+
def run_pipeline(filesystem: dict, task: str = "easy") -> dict:
|
| 405 |
+
"""
|
| 406 |
+
Top-level dispatcher. Call this from environment.py.
|
| 407 |
+
Returns {"exit_code": int, "logs": str}.
|
| 408 |
+
"""
|
| 409 |
+
if task == "medium":
|
| 410 |
+
return _run_medium_pipeline(filesystem)
|
| 411 |
+
elif task == "hard":
|
| 412 |
+
return _run_hard_pipeline(filesystem)
|
| 413 |
+
else:
|
| 414 |
+
return run_install_stage(filesystem, task=task)
|
inference.py
ADDED
|
@@ -0,0 +1,339 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Inference Script — CI/CD Doctor
|
| 3 |
+
===================================
|
| 4 |
+
MANDATORY environment variables:
|
| 5 |
+
API_BASE_URL LLM API endpoint (default: HuggingFace router)
|
| 6 |
+
MODEL_NAME Model identifier (default: Qwen2.5-72B-Instruct)
|
| 7 |
+
HF_TOKEN / API_KEY API key
|
| 8 |
+
IMAGE_NAME Docker image name (if using from_docker_image())
|
| 9 |
+
CICD_TASK Task difficulty: easy | medium | hard (default: easy)
|
| 10 |
+
|
| 11 |
+
STDOUT FORMAT
|
| 12 |
+
[START] task=<task> env=CI_CD_Doctor model=<model>
|
| 13 |
+
[STEP] step=<n> action=<cmd> pipeline_status=<status> reward=<0.00> done=<true|false> stdout=<preview> error=<msg|null>
|
| 14 |
+
[END] success=<true|false> steps=<n> score=<0.00> rewards=<r1,r2,...>
|
| 15 |
+
"""
|
| 16 |
+
|
| 17 |
+
import os
|
| 18 |
+
import asyncio
|
| 19 |
+
import textwrap
|
| 20 |
+
from typing import List, Optional
|
| 21 |
+
from dotenv import load_dotenv
|
| 22 |
+
|
| 23 |
+
from openai import OpenAI
|
| 24 |
+
|
| 25 |
+
from client import CiCdDoctorAction, CiCdDoctorEnv
|
| 26 |
+
|
| 27 |
+
load_dotenv()
|
| 28 |
+
|
| 29 |
+
IMAGE_NAME = os.getenv("IMAGE_NAME")
|
| 30 |
+
API_KEY = os.getenv("HF_TOKEN")
|
| 31 |
+
if API_KEY is None:
|
| 32 |
+
raise ValueError("API_KEY environment variable is required")
|
| 33 |
+
API_BASE_URL = os.getenv("API_BASE_URL", "https://router.huggingface.co/v1")
|
| 34 |
+
MODEL_NAME = os.getenv("MODEL_NAME", "Qwen/Qwen2.5-72B-Instruct")
|
| 35 |
+
TASK_NAME = os.getenv("CICD_TASK", "easy")
|
| 36 |
+
BENCHMARK = "CI_CD_Doctor"
|
| 37 |
+
MAX_STEPS_BY_TASK = {"easy": 10, "medium": 15, "hard": 25}
|
| 38 |
+
DEFAULT_MAX_STEPS = 15
|
| 39 |
+
EPISODES = [
|
| 40 |
+
{"task": "easy", "seed": 42},
|
| 41 |
+
{"task": "medium", "seed": 42},
|
| 42 |
+
{"task": "medium", "seed": 7},
|
| 43 |
+
{"task": "medium", "seed": 99},
|
| 44 |
+
{"task": "hard", "seed": 42},
|
| 45 |
+
]
|
| 46 |
+
TEMPERATURE = 0.5
|
| 47 |
+
MAX_TOKENS = 300
|
| 48 |
+
SUCCESS_SCORE_THRESHOLD = 0.8
|
| 49 |
+
SUCCESS_THRESHOLDS = {"easy": 0.70, "medium": 0.60, "hard": 0.45}
|
| 50 |
+
MIN_REWARD = 0.01
|
| 51 |
+
|
| 52 |
+
def clamp(value: float, lo: float = 0.01, hi: float = 0.99) -> float:
|
| 53 |
+
"""Clamp value to [lo, hi] inclusive."""
|
| 54 |
+
return max(lo, min(hi, value))
|
| 55 |
+
|
| 56 |
+
def _sanitize(text: str) -> str:
|
| 57 |
+
return " ".join((text or "").split())
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def _format_stdout_for_log(text: str, max_len: int = 500) -> str:
|
| 61 |
+
if not text:
|
| 62 |
+
return '""'
|
| 63 |
+
escaped = text.replace("\\", "\\\\").replace("\n", "\\n").replace("\r", "\\r")
|
| 64 |
+
if len(escaped) > max_len:
|
| 65 |
+
escaped = escaped[:max_len] + "..."
|
| 66 |
+
return f'"{escaped}"'
|
| 67 |
+
|
| 68 |
+
SYSTEM_PROMPT = textwrap.dedent(
|
| 69 |
+
"""
|
| 70 |
+
You are a DevOps engineer debugging a broken CI/CD pipeline.
|
| 71 |
+
Your goal: fix ALL issues so the pipeline passes within the step budget.
|
| 72 |
+
|
| 73 |
+
Available commands (output EXACTLY ONE per turn, nothing else):
|
| 74 |
+
cat <filename> — read a file
|
| 75 |
+
echo "<text>" >> <filename> — append a line to a file
|
| 76 |
+
sed -i 's/old/new/' <filename> — find-replace in a file (replaces ALL occurrences)
|
| 77 |
+
pipeline run — run the full pipeline
|
| 78 |
+
pipeline logs — show last pipeline logs
|
| 79 |
+
pipeline status — show current pass/fail status
|
| 80 |
+
|
| 81 |
+
DEBUGGING PROTOCOL:
|
| 82 |
+
|
| 83 |
+
1. DISCOVER — run `pipeline run` to see which stage fails.
|
| 84 |
+
The error output tells you what is expected and which file to inspect.
|
| 85 |
+
|
| 86 |
+
2. INVESTIGATE — `cat` the file mentioned in the error.
|
| 87 |
+
Read its current contents carefully.
|
| 88 |
+
|
| 89 |
+
3. DIAGNOSE — compare what the file contains against what the error says is required.
|
| 90 |
+
Identify the exact discrepancy. Do not guess — the logs contain the answer.
|
| 91 |
+
|
| 92 |
+
4. FIX — apply a targeted edit with echo or sed.
|
| 93 |
+
If you spotted problems in multiple files, fix all of them before re-running.
|
| 94 |
+
|
| 95 |
+
5. VERIFY — run `pipeline run` again.
|
| 96 |
+
If a new stage fails, repeat from step 2 for that stage.
|
| 97 |
+
If the same stage still fails, re-read the file — your edit may not have taken effect.
|
| 98 |
+
|
| 99 |
+
ANTI-LOOP RULES (strictly enforced):
|
| 100 |
+
• Never run `pipeline run` twice in a row without making an edit in between.
|
| 101 |
+
• Never `cat` a file you already read unless you edited it since.
|
| 102 |
+
• Never issue the same command twice in a row.
|
| 103 |
+
• One command per turn. Never chain commands on one line.
|
| 104 |
+
|
| 105 |
+
Output ONLY the raw command string. No explanation, no markdown, no backticks.
|
| 106 |
+
"""
|
| 107 |
+
).strip()
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
def log_start(task: str, env: str, model: str) -> None:
|
| 111 |
+
print(f"[START] task={task} env={env} model={model}", flush=True)
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
def log_step(
|
| 115 |
+
step: int,
|
| 116 |
+
action: str,
|
| 117 |
+
reward: float,
|
| 118 |
+
done: bool,
|
| 119 |
+
error: Optional[str],
|
| 120 |
+
) -> None:
|
| 121 |
+
error_val = _sanitize(error) if error else "null"
|
| 122 |
+
done_val = str(done).lower()
|
| 123 |
+
print(
|
| 124 |
+
f"[STEP] step={step} action={_sanitize(action)} reward={reward:.2f} done={done_val} error={error_val}",
|
| 125 |
+
flush=True,
|
| 126 |
+
)
|
| 127 |
+
|
| 128 |
+
|
| 129 |
+
def log_end(success: bool, steps: int, score: float, rewards: List[float]) -> None:
|
| 130 |
+
rewards_str = ",".join(f"{r:.2f}" for r in rewards)
|
| 131 |
+
print(
|
| 132 |
+
f"[END] success={str(success).lower()} steps={steps} score={score:.2f} rewards={rewards_str}",
|
| 133 |
+
flush=True,
|
| 134 |
+
)
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
def build_user_prompt(
|
| 138 |
+
step: int,
|
| 139 |
+
last_stdout: str,
|
| 140 |
+
pipeline_status: str,
|
| 141 |
+
history: List[str],
|
| 142 |
+
errors_seen: List[str],
|
| 143 |
+
files_read: dict,
|
| 144 |
+
edits_made: List[str],
|
| 145 |
+
max_steps: int = DEFAULT_MAX_STEPS,
|
| 146 |
+
) -> str:
|
| 147 |
+
history_block = "\n".join(history) if history else "None"
|
| 148 |
+
|
| 149 |
+
errors_block = "\n".join(f" - {e}" for e in errors_seen) if errors_seen else " (none yet)"
|
| 150 |
+
|
| 151 |
+
files_block = ""
|
| 152 |
+
if files_read:
|
| 153 |
+
for fname, content in files_read.items():
|
| 154 |
+
preview = content.strip()[:300]
|
| 155 |
+
files_block += f"\n [{fname}]:\n {preview}\n"
|
| 156 |
+
else:
|
| 157 |
+
files_block = " (none yet)"
|
| 158 |
+
|
| 159 |
+
edits_block = "\n".join(f" - {e}" for e in edits_made) if edits_made else " (none yet)"
|
| 160 |
+
|
| 161 |
+
return textwrap.dedent(
|
| 162 |
+
f"""
|
| 163 |
+
Step {step} of {max_steps} | Pipeline: {pipeline_status}
|
| 164 |
+
|
| 165 |
+
Last command output:
|
| 166 |
+
{last_stdout.strip() or "(no output)"}
|
| 167 |
+
|
| 168 |
+
=== SESSION CONTEXT ===
|
| 169 |
+
Errors seen so far:
|
| 170 |
+
{errors_block}
|
| 171 |
+
|
| 172 |
+
Files read (latest content):
|
| 173 |
+
{files_block}
|
| 174 |
+
Edits applied so far:
|
| 175 |
+
{edits_block}
|
| 176 |
+
|
| 177 |
+
Full action history:
|
| 178 |
+
{history_block}
|
| 179 |
+
|
| 180 |
+
What is your next command?
|
| 181 |
+
"""
|
| 182 |
+
).strip()
|
| 183 |
+
|
| 184 |
+
def get_agent_command(
|
| 185 |
+
client: OpenAI,
|
| 186 |
+
step: int,
|
| 187 |
+
last_stdout: str,
|
| 188 |
+
pipeline_status: str,
|
| 189 |
+
history: List[str],
|
| 190 |
+
errors_seen: List[str],
|
| 191 |
+
files_read: dict,
|
| 192 |
+
edits_made: List[str],
|
| 193 |
+
max_steps: int = DEFAULT_MAX_STEPS,
|
| 194 |
+
) -> str:
|
| 195 |
+
user_prompt = build_user_prompt(
|
| 196 |
+
step, last_stdout, pipeline_status, history,
|
| 197 |
+
errors_seen, files_read, edits_made, max_steps=max_steps,
|
| 198 |
+
)
|
| 199 |
+
try:
|
| 200 |
+
completion = client.chat.completions.create(
|
| 201 |
+
model=MODEL_NAME,
|
| 202 |
+
messages=[
|
| 203 |
+
{"role": "system", "content": SYSTEM_PROMPT},
|
| 204 |
+
{"role": "user", "content": user_prompt},
|
| 205 |
+
],
|
| 206 |
+
temperature=TEMPERATURE,
|
| 207 |
+
max_tokens=MAX_TOKENS,
|
| 208 |
+
stream=False,
|
| 209 |
+
)
|
| 210 |
+
text = (completion.choices[0].message.content or "").strip()
|
| 211 |
+
return text if text else "pipeline status"
|
| 212 |
+
except Exception as exc:
|
| 213 |
+
print(f"[DEBUG] Model request failed: {exc}", flush=True)
|
| 214 |
+
return "pipeline status"
|
| 215 |
+
|
| 216 |
+
|
| 217 |
+
def _extract_errors(stdout: str) -> List[str]:
|
| 218 |
+
"""Pull error lines out of pipeline output."""
|
| 219 |
+
errors = []
|
| 220 |
+
for line in stdout.splitlines():
|
| 221 |
+
line = line.strip()
|
| 222 |
+
if line.upper().startswith("ERROR") or "error" in line.lower():
|
| 223 |
+
if line and line not in errors:
|
| 224 |
+
errors.append(line)
|
| 225 |
+
return errors
|
| 226 |
+
|
| 227 |
+
|
| 228 |
+
async def run_episode(client: OpenAI, env, task: str, seed: int) -> dict:
|
| 229 |
+
"""Run a single episode. Returns {"score", "steps", "rewards", "success"}."""
|
| 230 |
+
history: List[str] = []
|
| 231 |
+
rewards: List[float] = []
|
| 232 |
+
errors_seen: List[str] = [] # accumulated distinct errors
|
| 233 |
+
files_read: dict = {} # filename → last known content
|
| 234 |
+
edits_made: List[str] = [] # human-readable edit log
|
| 235 |
+
steps_taken = 0
|
| 236 |
+
max_steps = MAX_STEPS_BY_TASK.get(task, DEFAULT_MAX_STEPS)
|
| 237 |
+
|
| 238 |
+
result = await env.reset(task=task, seed=seed)
|
| 239 |
+
last_stdout = result.observation.stdout
|
| 240 |
+
pipeline_status = result.observation.pipeline_status
|
| 241 |
+
|
| 242 |
+
for step in range(1, max_steps + 1):
|
| 243 |
+
if result.done:
|
| 244 |
+
break
|
| 245 |
+
|
| 246 |
+
command = get_agent_command(
|
| 247 |
+
client, step, last_stdout, pipeline_status, history,
|
| 248 |
+
errors_seen, files_read, edits_made, max_steps=max_steps,
|
| 249 |
+
)
|
| 250 |
+
error: Optional[str] = None
|
| 251 |
+
step_stdout = ""
|
| 252 |
+
done = False
|
| 253 |
+
reward = MIN_REWARD
|
| 254 |
+
try:
|
| 255 |
+
result = await env.step(CiCdDoctorAction(command=command))
|
| 256 |
+
obs = result.observation
|
| 257 |
+
reward = result.reward #clamp(result.reward or 0.0)
|
| 258 |
+
done = result.done
|
| 259 |
+
last_stdout = obs.stdout
|
| 260 |
+
step_stdout = obs.stdout
|
| 261 |
+
pipeline_status = obs.pipeline_status
|
| 262 |
+
|
| 263 |
+
# Accumulate session context
|
| 264 |
+
cmd_lower = command.strip()
|
| 265 |
+
if cmd_lower.startswith("pipeline run"):
|
| 266 |
+
for err in _extract_errors(step_stdout):
|
| 267 |
+
if err not in errors_seen:
|
| 268 |
+
errors_seen.append(err)
|
| 269 |
+
elif cmd_lower.startswith("cat "):
|
| 270 |
+
fname = cmd_lower[4:].strip()
|
| 271 |
+
files_read[fname] = step_stdout
|
| 272 |
+
elif cmd_lower.startswith("echo ") or cmd_lower.startswith("sed "):
|
| 273 |
+
edits_made.append(f"Step {step}: {command}")
|
| 274 |
+
|
| 275 |
+
except Exception as exc:
|
| 276 |
+
reward = 0.01
|
| 277 |
+
done = True
|
| 278 |
+
error = f"{type(exc).__name__}: {exc}"
|
| 279 |
+
|
| 280 |
+
rewards.append(reward)
|
| 281 |
+
steps_taken = step
|
| 282 |
+
log_step(
|
| 283 |
+
step=step,
|
| 284 |
+
action=command,
|
| 285 |
+
reward=reward,
|
| 286 |
+
done=done,
|
| 287 |
+
error=error,
|
| 288 |
+
)
|
| 289 |
+
history.append(
|
| 290 |
+
f"Step {step}: {command!r} -> status={pipeline_status} reward={reward:+.2f}"
|
| 291 |
+
)
|
| 292 |
+
|
| 293 |
+
if done:
|
| 294 |
+
break
|
| 295 |
+
|
| 296 |
+
score = clamp(sum(rewards) if rewards else MIN_REWARD)
|
| 297 |
+
threshold = SUCCESS_THRESHOLDS.get(task, SUCCESS_SCORE_THRESHOLD)
|
| 298 |
+
return {"score": score, "steps": steps_taken, "rewards": rewards, "success": score >= threshold}
|
| 299 |
+
|
| 300 |
+
|
| 301 |
+
async def main() -> None:
|
| 302 |
+
client = OpenAI(base_url=API_BASE_URL, api_key=API_KEY)
|
| 303 |
+
env = None
|
| 304 |
+
|
| 305 |
+
try:
|
| 306 |
+
if IMAGE_NAME:
|
| 307 |
+
env = await CiCdDoctorEnv.from_docker_image(IMAGE_NAME)
|
| 308 |
+
else:
|
| 309 |
+
base_url = os.getenv("ENV_BASE_URL", "http://localhost:8000")
|
| 310 |
+
env = CiCdDoctorEnv(base_url=base_url)
|
| 311 |
+
|
| 312 |
+
all_rewards: List[float] = []
|
| 313 |
+
all_steps = 0
|
| 314 |
+
|
| 315 |
+
for ep in EPISODES:
|
| 316 |
+
task, seed = ep["task"], ep["seed"]
|
| 317 |
+
log_start(task=task, env=BENCHMARK, model=MODEL_NAME)
|
| 318 |
+
result = await run_episode(client, env, task=task, seed=seed)
|
| 319 |
+
all_rewards.extend(result["rewards"])
|
| 320 |
+
all_steps += result["steps"]
|
| 321 |
+
log_end(
|
| 322 |
+
success=result["success"],
|
| 323 |
+
steps=result["steps"],
|
| 324 |
+
score=result["score"],
|
| 325 |
+
rewards=result["rewards"],
|
| 326 |
+
)
|
| 327 |
+
|
| 328 |
+
except Exception as exc:
|
| 329 |
+
print(f"[DEBUG] fatal error: {type(exc).__name__}: {exc}", flush=True)
|
| 330 |
+
finally:
|
| 331 |
+
if env is not None:
|
| 332 |
+
try:
|
| 333 |
+
await env.close()
|
| 334 |
+
except Exception as e:
|
| 335 |
+
print(f"[DEBUG] env.close() error: {e}", flush=True)
|
| 336 |
+
|
| 337 |
+
|
| 338 |
+
if __name__ == "__main__":
|
| 339 |
+
asyncio.run(main())
|
models.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Data models for the CI/CD Doctor RL environment.
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
from pydantic import BaseModel, Field
|
| 6 |
+
from typing import Optional, Dict, Any, List
|
| 7 |
+
|
| 8 |
+
|
| 9 |
+
class PipelineAction(BaseModel):
|
| 10 |
+
command: str # raw string the agent types, e.g. "cat requirements.txt"2
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
class PipelineObservation(BaseModel):
|
| 14 |
+
stdout: str
|
| 15 |
+
exit_code: int
|
| 16 |
+
pipeline_status: str # "not_run" | "running" | "passed" | "failed"
|
| 17 |
+
steps_remaining: int
|
| 18 |
+
done: bool = False
|
| 19 |
+
reward: float = 0.0
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
class PipelineState(BaseModel):
|
| 23 |
+
episode_id: str
|
| 24 |
+
task: str # "easy" | "medium" | "hard"
|
| 25 |
+
filesystem: Dict[str, str]
|
| 26 |
+
pipeline_status: str
|
| 27 |
+
step_count: int
|
| 28 |
+
done: bool
|
| 29 |
+
total_reward: float
|
| 30 |
+
answer_key: Dict[str, Any] # never sent to agent, used by grader
|
| 31 |
+
milestones: List[str] = Field(default_factory=list) # grader-only, tracks unlocked reward tiers
|
openenv.yaml
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
spec_version: 1
|
| 2 |
+
name: CI_CD_Doctor
|
| 3 |
+
type: space
|
| 4 |
+
runtime: fastapi
|
| 5 |
+
app: server.app:app
|
| 6 |
+
port: 8000
|
| 7 |
+
|
| 8 |
+
tasks:
|
| 9 |
+
- id: task_easy
|
| 10 |
+
difficulty: easy
|
| 11 |
+
max_steps: 10
|
| 12 |
+
description: "Single-file failure: one required package is missing from requirements.txt. Inspect the install logs, identify the missing package, and add it."
|
| 13 |
+
grader:
|
| 14 |
+
type: programmatic
|
| 15 |
+
module: environment.grader:grade
|
| 16 |
+
success_threshold: 0.70
|
| 17 |
+
prompt_template: |
|
| 18 |
+
Score the agent's CI/CD debugging session from 0.01 to 0.99
|
| 19 |
+
Task: fix a single missing package in requirements.txt so the install stage passes.
|
| 20 |
+
Full credit (0.99) requires the pipeline to reach `passed` state.
|
| 21 |
+
Partial credit is awarded per correct fix landed in the target file.
|
| 22 |
+
Penalize redundant reads, idle pipeline re-runs, and exceeding the ideal step budget.
|
| 23 |
+
|
| 24 |
+
- id: task_medium
|
| 25 |
+
difficulty: medium
|
| 26 |
+
max_steps: 15
|
| 27 |
+
description: "Two-file cascading failure (one of four randomized variants). Each variant pairs two bugs across files such as Dockerfile (wrong Python version or port), .env.ci (missing required env var), requirements.txt (missing package), deploy_config.yml (deploy_enabled false), Makefile (wrong test command), or service.yaml (wrong port). Bugs surface one at a time across the pipeline stages — fix both to make it pass."
|
| 28 |
+
grader:
|
| 29 |
+
type: programmatic
|
| 30 |
+
module: environment.grader:grade
|
| 31 |
+
success_threshold: 0.60
|
| 32 |
+
prompt_template: |
|
| 33 |
+
Score the agent's CI/CD debugging session from 0.01 to 0.99
|
| 34 |
+
Task: diagnose and fix TWO bugs spread across two files in one of four
|
| 35 |
+
structurally distinct medium variants. Each pipeline run only reveals
|
| 36 |
+
the next failing stage — the agent must iterate discover→diagnose→fix→verify.
|
| 37 |
+
Full credit (0.99) requires the pipeline to reach `passed` state.
|
| 38 |
+
Award partial credit per fix landed in the correct file.
|
| 39 |
+
Penalize redundant reads, idle pipeline re-runs, exploitation traps
|
| 40 |
+
(knows the bug but won't act), and exceeding the ideal step budget.
|
| 41 |
+
|
| 42 |
+
- id: task_hard
|
| 43 |
+
difficulty: hard
|
| 44 |
+
max_steps: 25
|
| 45 |
+
description: "Three-file cascading failure (one of four randomized variants). Each variant chains three bugs across files such as ci.yml (wrong stage order), Dockerfile (alpine base incompatible with binary wheels), requirements.txt (missing package or wrong version pin like numpy==1.21), .env.ci (missing env var), Makefile (wrong test command), deploy_config.yml (deploy_enabled false), or service.yaml (wrong port). Each pipeline run stops at the first failing stage, so bugs reveal themselves one at a time — fix all three in sequence."
|
| 46 |
+
grader:
|
| 47 |
+
type: programmatic
|
| 48 |
+
module: environment.grader:grade
|
| 49 |
+
success_threshold: 0.45
|
| 50 |
+
prompt_template: |
|
| 51 |
+
Score the agent's CI/CD debugging session from 0.01 to 0.99
|
| 52 |
+
Task: diagnose and fix THREE cascading bugs across three files in one of
|
| 53 |
+
four hard variants. Bugs surface one at a time as earlier stages pass.
|
| 54 |
+
Some fixes require reasoning about error messages (e.g. alpine base
|
| 55 |
+
image lacks glibc for native deps; numpy==1.21 pin conflicts with
|
| 56 |
+
transitive numpy>=1.26 requirement) — exact-string matching alone is
|
| 57 |
+
insufficient.
|
| 58 |
+
Full credit (0.99) requires the pipeline to reach `passed` state.
|
| 59 |
+
Award partial credit per fix landed in the correct file.
|
| 60 |
+
Penalize redundant reads, idle pipeline re-runs, exploitation traps,
|
| 61 |
+
and exceeding the ideal step budget.
|
openenv_CI_CD_Doctor.egg-info/PKG-INFO
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.4
|
| 2 |
+
Name: openenv-CI_CD_Doctor
|
| 3 |
+
Version: 0.1.0
|
| 4 |
+
Summary: Ci Cd Doctor environment for OpenEnv
|
| 5 |
+
Requires-Python: >=3.10
|
| 6 |
+
License-File: LICENSE
|
| 7 |
+
Requires-Dist: openenv-core[core]>=0.2.2
|
| 8 |
+
Provides-Extra: dev
|
| 9 |
+
Requires-Dist: pytest>=8.0.0; extra == "dev"
|
| 10 |
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
| 11 |
+
Dynamic: license-file
|
openenv_CI_CD_Doctor.egg-info/SOURCES.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
LICENSE
|
| 2 |
+
README.md
|
| 3 |
+
pyproject.toml
|
| 4 |
+
./__init__.py
|
| 5 |
+
./inference.py
|
| 6 |
+
environment/__init__.py
|
| 7 |
+
environment/client.py
|
| 8 |
+
environment/generator.py
|
| 9 |
+
environment/grader.py
|
| 10 |
+
environment/models.py
|
| 11 |
+
environment/packages.py
|
| 12 |
+
environment/parser.py
|
| 13 |
+
environment/stage_runner.py
|
| 14 |
+
openenv_CI_CD_Doctor.egg-info/PKG-INFO
|
| 15 |
+
openenv_CI_CD_Doctor.egg-info/SOURCES.txt
|
| 16 |
+
openenv_CI_CD_Doctor.egg-info/dependency_links.txt
|
| 17 |
+
openenv_CI_CD_Doctor.egg-info/entry_points.txt
|
| 18 |
+
openenv_CI_CD_Doctor.egg-info/requires.txt
|
| 19 |
+
openenv_CI_CD_Doctor.egg-info/top_level.txt
|
| 20 |
+
server/__init__.py
|
| 21 |
+
server/app.py
|
| 22 |
+
server/environment.py
|
openenv_CI_CD_Doctor.egg-info/dependency_links.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
|
openenv_CI_CD_Doctor.egg-info/entry_points.txt
ADDED
|
@@ -0,0 +1,2 @@
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[console_scripts]
|
| 2 |
+
server = CI_CD_Doctor.server.app:main
|
openenv_CI_CD_Doctor.egg-info/requires.txt
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
openenv-core[core]>=0.2.2
|
| 2 |
+
|
| 3 |
+
[dev]
|
| 4 |
+
pytest>=8.0.0
|
| 5 |
+
pytest-cov>=4.0.0
|
openenv_CI_CD_Doctor.egg-info/top_level.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
CI_CD_Doctor
|
pyproject.toml
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright (c) Meta Platforms, Inc. and affiliates.
|
| 2 |
+
# All rights reserved.
|
| 3 |
+
#
|
| 4 |
+
# This source code is licensed under the BSD-style license found in the
|
| 5 |
+
# LICENSE file in the root directory of this source tree.
|
| 6 |
+
|
| 7 |
+
[build-system]
|
| 8 |
+
requires = ["setuptools>=45", "wheel"]
|
| 9 |
+
build-backend = "setuptools.build_meta"
|
| 10 |
+
|
| 11 |
+
[project]
|
| 12 |
+
name = "openenv-CI_CD_Doctor"
|
| 13 |
+
version = "0.1.0"
|
| 14 |
+
description = "Ci Cd Doctor environment for OpenEnv"
|
| 15 |
+
requires-python = ">=3.10"
|
| 16 |
+
dependencies = [
|
| 17 |
+
# Core OpenEnv runtime (provides FastAPI server + HTTP client types)
|
| 18 |
+
# install from github
|
| 19 |
+
# "openenv-core[core] @ git+https://github.com/meta-pytorch/OpenEnv.git",
|
| 20 |
+
"openenv-core[core]>=0.2.2",
|
| 21 |
+
# Environment-specific dependencies
|
| 22 |
+
# Add all dependencies needed for your environment here
|
| 23 |
+
# Examples:
|
| 24 |
+
# "numpy>=1.19.0",
|
| 25 |
+
# "torch>=2.0.0",
|
| 26 |
+
# "gymnasium>=0.29.0",
|
| 27 |
+
# "openspiel>=1.0.0",
|
| 28 |
+
# "smolagents>=1.22.0,<2",
|
| 29 |
+
]
|
| 30 |
+
|
| 31 |
+
[project.optional-dependencies]
|
| 32 |
+
dev = [
|
| 33 |
+
"pytest>=8.0.0",
|
| 34 |
+
"pytest-cov>=4.0.0",
|
| 35 |
+
]
|
| 36 |
+
|
| 37 |
+
[project.scripts]
|
| 38 |
+
# Server entry point - enables running via: uv run --project . server
|
| 39 |
+
# or: python -m CI_CD_Doctor.server.app
|
| 40 |
+
server = "CI_CD_Doctor.server.app:main"
|
| 41 |
+
|
| 42 |
+
[tool.setuptools]
|
| 43 |
+
include-package-data = true
|
| 44 |
+
packages = ["CI_CD_Doctor", "CI_CD_Doctor.environment", "CI_CD_Doctor.server"]
|
| 45 |
+
package-dir = { "CI_CD_Doctor" = ".", "CI_CD_Doctor.environment" = "environment", "CI_CD_Doctor.server" = "server" }
|
requirements.txt
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
openenv[core]>=0.2.0
|
| 2 |
+
fastapi>=0.115.0
|
| 3 |
+
uvicorn>=0.24.0
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
|
server/__init__.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""CI/CD Doctor server subpackage."""
|
| 2 |
+
|
| 3 |
+
from .environment import PipelineEnvironment
|
| 4 |
+
|
| 5 |
+
__all__ = ["PipelineEnvironment"]
|
server/app.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
FastAPI application for the CI/CD Doctor environment.
|
| 3 |
+
|
| 4 |
+
Wraps PipelineEnvironment in an OpenEnv-compatible interface so it can be
|
| 5 |
+
served via openenv's create_app() infrastructure.
|
| 6 |
+
"""
|
| 7 |
+
|
| 8 |
+
try:
|
| 9 |
+
from openenv.core.env_server.http_server import create_app
|
| 10 |
+
from openenv.core.env_server.interfaces import Environment
|
| 11 |
+
from openenv.core.env_server.types import Action, Observation, State
|
| 12 |
+
except Exception as e: # pragma: no cover
|
| 13 |
+
raise ImportError(
|
| 14 |
+
"openenv is required for the web interface. Install dependencies with '\n uv sync\n'"
|
| 15 |
+
) from e
|
| 16 |
+
|
| 17 |
+
from uuid import uuid4
|
| 18 |
+
from pydantic import Field
|
| 19 |
+
|
| 20 |
+
from models import PipelineObservation
|
| 21 |
+
from .environment import PipelineEnvironment
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
# OpenEnv-compatible action — wraps the agent's raw command string
|
| 25 |
+
class CiCdDoctorAction(Action):
|
| 26 |
+
command: str = Field(..., description="Shell-like command the agent issues")
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
# OpenEnv-compatible observation — extends base Observation with pipeline fields
|
| 30 |
+
class CiCdDoctorObservation(Observation):
|
| 31 |
+
stdout: str = Field(default="", description="Command output / logs seen by agent")
|
| 32 |
+
exit_code: int = Field(default=0, description="0 = success, 1 = error")
|
| 33 |
+
pipeline_status: str = Field(default="not_run", description="not_run | running | passed | failed")
|
| 34 |
+
steps_remaining: int = Field(default=15, description="Steps left in episode")
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
def _to_obs(obs: PipelineObservation) -> CiCdDoctorObservation:
|
| 38 |
+
return CiCdDoctorObservation(
|
| 39 |
+
stdout=obs.stdout,
|
| 40 |
+
exit_code=obs.exit_code,
|
| 41 |
+
pipeline_status=obs.pipeline_status,
|
| 42 |
+
steps_remaining=obs.steps_remaining,
|
| 43 |
+
done=obs.done,
|
| 44 |
+
reward=obs.reward,
|
| 45 |
+
)
|
| 46 |
+
|
| 47 |
+
|
| 48 |
+
class CiCdDoctorEnvironment(Environment):
|
| 49 |
+
"""OpenEnv adapter that wraps PipelineEnvironment."""
|
| 50 |
+
|
| 51 |
+
SUPPORTS_CONCURRENT_SESSIONS: bool = True
|
| 52 |
+
|
| 53 |
+
def __init__(self):
|
| 54 |
+
self._env = PipelineEnvironment()
|
| 55 |
+
self._state_obj = State(episode_id=str(uuid4()), step_count=0)
|
| 56 |
+
|
| 57 |
+
def reset(self, task: str = "easy", seed: int = 42) -> CiCdDoctorObservation:
|
| 58 |
+
obs = self._env.reset(task=task, seed=seed)
|
| 59 |
+
s = self._env.state()
|
| 60 |
+
self._state_obj = State(episode_id=s.episode_id, step_count=s.step_count)
|
| 61 |
+
return _to_obs(obs)
|
| 62 |
+
|
| 63 |
+
def step(self, action: CiCdDoctorAction) -> CiCdDoctorObservation: # type: ignore[override]
|
| 64 |
+
from models import PipelineAction
|
| 65 |
+
obs = self._env.step(PipelineAction(command=action.command))
|
| 66 |
+
s = self._env.state()
|
| 67 |
+
self._state_obj = State(episode_id=s.episode_id, step_count=s.step_count)
|
| 68 |
+
return _to_obs(obs)
|
| 69 |
+
|
| 70 |
+
@property
|
| 71 |
+
def state(self) -> State:
|
| 72 |
+
return self._state_obj
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
app = create_app(
|
| 76 |
+
CiCdDoctorEnvironment,
|
| 77 |
+
CiCdDoctorAction,
|
| 78 |
+
CiCdDoctorObservation,
|
| 79 |
+
env_name="CI_CD_Doctor",
|
| 80 |
+
max_concurrent_envs=1,
|
| 81 |
+
)
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def main():
|
| 85 |
+
import uvicorn
|
| 86 |
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
if __name__ == "__main__":
|
| 90 |
+
import argparse
|
| 91 |
+
parser = argparse.ArgumentParser()
|
| 92 |
+
parser.add_argument("--port", type=int, default=8000)
|
| 93 |
+
args = parser.parse_args()
|
| 94 |
+
import uvicorn
|
| 95 |
+
uvicorn.run(app, host="0.0.0.0", port=args.port)
|
server/app_2.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import os
|
| 2 |
+
from fastapi import FastAPI, Request, HTTPException
|
| 3 |
+
import gradio as gr
|
| 4 |
+
|
| 5 |
+
from client import CiCdDoctorEnv, CiCdDoctorAction
|
| 6 |
+
from models import PipelineObservation
|
| 7 |
+
|
| 8 |
+
# Optional: your existing Gradio UI
|
| 9 |
+
try:
|
| 10 |
+
from app import demo
|
| 11 |
+
except ImportError:
|
| 12 |
+
demo = None
|
| 13 |
+
|
| 14 |
+
app = FastAPI(
|
| 15 |
+
title="CI/CD Doctor",
|
| 16 |
+
description="Self-healing CI/CD pipeline agent",
|
| 17 |
+
version="1.0.0",
|
| 18 |
+
)
|
| 19 |
+
|
| 20 |
+
_env = None
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
@app.get("/health")
|
| 24 |
+
async def health():
|
| 25 |
+
return {"status": "healthy"}
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
@app.get("/metadata")
|
| 29 |
+
async def metadata():
|
| 30 |
+
return {
|
| 31 |
+
"name": "CI/CD Doctor",
|
| 32 |
+
"description": "Fix broken CI/CD pipelines autonomously",
|
| 33 |
+
"version": "1.0.0",
|
| 34 |
+
"submission_type": "openenv-v4",
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
@app.get("/schema")
|
| 39 |
+
async def schema():
|
| 40 |
+
return {
|
| 41 |
+
"action": CiCdDoctorAction.model_json_schema(),
|
| 42 |
+
"observation": PipelineObservation.model_json_schema(),
|
| 43 |
+
"state": {"type": "object"},
|
| 44 |
+
}
|
| 45 |
+
|
| 46 |
+
|
| 47 |
+
@app.post("/reset")
|
| 48 |
+
async def reset(request: Request):
|
| 49 |
+
global _env
|
| 50 |
+
try:
|
| 51 |
+
body = await request.json() if await request.body() else {}
|
| 52 |
+
task = body.get("task", "easy")
|
| 53 |
+
seed = body.get("seed", 42)
|
| 54 |
+
|
| 55 |
+
base_url = os.getenv("ENV_BASE_URL", "http://localhost:8000")
|
| 56 |
+
_env = await CiCdDoctorEnv(base_url=base_url)
|
| 57 |
+
|
| 58 |
+
result = await _env.reset(task=task, seed=seed)
|
| 59 |
+
return result.observation.model_dump()
|
| 60 |
+
|
| 61 |
+
except Exception as e:
|
| 62 |
+
raise HTTPException(status_code=500, detail=str(e))
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
@app.post("/step")
|
| 66 |
+
async def step(request: Request):
|
| 67 |
+
global _env
|
| 68 |
+
if _env is None:
|
| 69 |
+
raise HTTPException(status_code=400, detail="Call /reset first")
|
| 70 |
+
|
| 71 |
+
try:
|
| 72 |
+
body = await request.json()
|
| 73 |
+
action = CiCdDoctorAction(**body)
|
| 74 |
+
|
| 75 |
+
result = await _env.step(action)
|
| 76 |
+
return result.observation.model_dump()
|
| 77 |
+
|
| 78 |
+
except Exception as e:
|
| 79 |
+
raise HTTPException(status_code=500, detail=str(e))
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
@app.get("/state")
|
| 83 |
+
async def state():
|
| 84 |
+
global _env
|
| 85 |
+
if _env is None:
|
| 86 |
+
return {"status": "uninitialized"}
|
| 87 |
+
return {"status": "running"}
|
| 88 |
+
|
| 89 |
+
|
| 90 |
+
# Mount Gradio UI if available
|
| 91 |
+
if demo is not None:
|
| 92 |
+
app = gr.mount_gradio_app(app, demo, path="/")
|
| 93 |
+
|
| 94 |
+
|
| 95 |
+
def main():
|
| 96 |
+
import uvicorn
|
| 97 |
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
| 98 |
+
|
| 99 |
+
|
| 100 |
+
if __name__ == "__main__":
|
| 101 |
+
main()
|
server/environment.py
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Core RL environment for the CI/CD Doctor.
|
| 3 |
+
Ties together generator, stage_runner, and parser into a step/reset/state loop.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
import uuid
|
| 7 |
+
|
| 8 |
+
from models import PipelineAction, PipelineObservation, PipelineState
|
| 9 |
+
from environment.generator import generate_easy_scenario, generate_medium_scenario, generate_hard_scenario
|
| 10 |
+
from environment.stage_runner import run_pipeline
|
| 11 |
+
from environment.parser import parse_command
|
| 12 |
+
from environment.grader import grade as grade_state, balance_score, StepContext
|
| 13 |
+
|
| 14 |
+
MAX_STEPS_BY_TASK = {"easy": 10, "medium": 15, "hard": 25}
|
| 15 |
+
IDEAL_STEPS_BY_TASK = {"easy": 3, "medium": 6, "hard": 10}
|
| 16 |
+
DEFAULT_MAX_STEPS = 15
|
| 17 |
+
DEFAULT_IDEAL_STEPS = 6
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
class PipelineEnvironment:
|
| 21 |
+
|
| 22 |
+
def reset(self, task: str = "easy", seed: int = 42) -> PipelineObservation:
|
| 23 |
+
if task == "medium":
|
| 24 |
+
scenario = generate_medium_scenario(seed)
|
| 25 |
+
elif task == "hard":
|
| 26 |
+
scenario = generate_hard_scenario(seed)
|
| 27 |
+
else:
|
| 28 |
+
scenario = generate_easy_scenario(seed)
|
| 29 |
+
|
| 30 |
+
self._filesystem = scenario["filesystem"]
|
| 31 |
+
self._answer_key = scenario["answer_key"]
|
| 32 |
+
self._task = task
|
| 33 |
+
self._max_steps = MAX_STEPS_BY_TASK.get(task, DEFAULT_MAX_STEPS)
|
| 34 |
+
self._ideal_steps = IDEAL_STEPS_BY_TASK.get(task, DEFAULT_IDEAL_STEPS)
|
| 35 |
+
self._step_count = 0
|
| 36 |
+
self._done = False
|
| 37 |
+
self._total_reward = 0.0
|
| 38 |
+
self._pipeline_status = "not_run"
|
| 39 |
+
self._episode_id = str(uuid.uuid4())
|
| 40 |
+
self._last_logs: dict = {}
|
| 41 |
+
self._milestones: set[str] = set()
|
| 42 |
+
self._files_read: set[str] = set()
|
| 43 |
+
self._fs_snapshot_at_last_run: str | None = None # None = no run yet
|
| 44 |
+
self._pipeline_runs_since_last_edit: int = 0
|
| 45 |
+
self._last_score = grade_state(self.state())
|
| 46 |
+
|
| 47 |
+
files = ", ".join(sorted(self._filesystem.keys()))
|
| 48 |
+
return PipelineObservation(
|
| 49 |
+
stdout=(
|
| 50 |
+
"Pipeline environment ready. The pipeline is failing. "
|
| 51 |
+
f"Files: {files}. Investigate and fix it."
|
| 52 |
+
),
|
| 53 |
+
exit_code=0,
|
| 54 |
+
pipeline_status=self._pipeline_status,
|
| 55 |
+
steps_remaining=self._max_steps,
|
| 56 |
+
done=False,
|
| 57 |
+
reward=0.0,
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
def step(self, action: PipelineAction) -> PipelineObservation:
|
| 61 |
+
if self._done:
|
| 62 |
+
raise RuntimeError("Episode is done. Call reset() first.")
|
| 63 |
+
|
| 64 |
+
self._step_count += 1
|
| 65 |
+
steps_remaining = self._max_steps - self._step_count
|
| 66 |
+
cmd = parse_command(action.command)
|
| 67 |
+
stdout = ""
|
| 68 |
+
exit_code = 0
|
| 69 |
+
|
| 70 |
+
if cmd.type == "cat":
|
| 71 |
+
content = self._filesystem.get(cmd.filename)
|
| 72 |
+
if content is None:
|
| 73 |
+
stdout = f"cat: {cmd.filename}: No such file or directory"
|
| 74 |
+
exit_code = 1
|
| 75 |
+
else:
|
| 76 |
+
stdout = content
|
| 77 |
+
|
| 78 |
+
elif cmd.type == "echo_append":
|
| 79 |
+
if cmd.filename in self._filesystem:
|
| 80 |
+
self._filesystem[cmd.filename] += cmd.content + "\n"
|
| 81 |
+
stdout = ""
|
| 82 |
+
else:
|
| 83 |
+
stdout = f"bash: {cmd.filename}: No such file or directory"
|
| 84 |
+
exit_code = 1
|
| 85 |
+
|
| 86 |
+
elif cmd.type == "sed":
|
| 87 |
+
if cmd.filename in self._filesystem:
|
| 88 |
+
self._filesystem[cmd.filename] = self._filesystem[cmd.filename].replace(
|
| 89 |
+
cmd.pattern, cmd.replacement
|
| 90 |
+
)
|
| 91 |
+
stdout = ""
|
| 92 |
+
else:
|
| 93 |
+
stdout = f"sed: {cmd.filename}: No such file or directory"
|
| 94 |
+
exit_code = 1
|
| 95 |
+
|
| 96 |
+
elif cmd.type == "pipeline_run":
|
| 97 |
+
result = run_pipeline(self._filesystem, task=self._task)
|
| 98 |
+
self._last_logs["last"] = result["logs"]
|
| 99 |
+
self._filesystem["logs/install.log"] = result["logs"]
|
| 100 |
+
self._pipeline_status = "passed" if result["exit_code"] == 0 else "failed"
|
| 101 |
+
stdout = result["logs"]
|
| 102 |
+
exit_code = result["exit_code"]
|
| 103 |
+
|
| 104 |
+
elif cmd.type == "pipeline_logs":
|
| 105 |
+
if cmd.stage:
|
| 106 |
+
stdout = self._last_logs.get(
|
| 107 |
+
cmd.stage,
|
| 108 |
+
self._last_logs.get("last", f"No logs for stage '{cmd.stage}' yet. Run pipeline first."),
|
| 109 |
+
)
|
| 110 |
+
else:
|
| 111 |
+
stdout = self._last_logs.get("last", "No pipeline runs yet.")
|
| 112 |
+
|
| 113 |
+
elif cmd.type == "pipeline_status":
|
| 114 |
+
stdout = f"Pipeline status: {self._pipeline_status}"
|
| 115 |
+
|
| 116 |
+
else:
|
| 117 |
+
stdout = f"Command not recognized: {action.command}"
|
| 118 |
+
exit_code = 1
|
| 119 |
+
|
| 120 |
+
# Capture context BEFORE updating tracking state
|
| 121 |
+
ctx = StepContext(
|
| 122 |
+
cmd_type=cmd.type,
|
| 123 |
+
filename=getattr(cmd, "filename", None),
|
| 124 |
+
files_read=set(self._files_read),
|
| 125 |
+
fs_changed_since_last_run=(
|
| 126 |
+
self._fs_snapshot_at_last_run is None
|
| 127 |
+
or repr(self._filesystem) != self._fs_snapshot_at_last_run
|
| 128 |
+
),
|
| 129 |
+
step_count=self._step_count,
|
| 130 |
+
max_steps=self._max_steps,
|
| 131 |
+
ideal_steps=self._ideal_steps,
|
| 132 |
+
pipeline_runs_since_last_edit=self._pipeline_runs_since_last_edit,
|
| 133 |
+
)
|
| 134 |
+
|
| 135 |
+
self._update_milestones(cmd)
|
| 136 |
+
current_score = grade_state(self.state())
|
| 137 |
+
grade_delta = round(current_score - self._last_score, 2)
|
| 138 |
+
self._last_score = current_score
|
| 139 |
+
|
| 140 |
+
shaped = balance_score(self.state(), ctx)
|
| 141 |
+
reward = round(grade_delta + shaped, 2)
|
| 142 |
+
self._total_reward += reward
|
| 143 |
+
|
| 144 |
+
if cmd.type == "cat" and exit_code == 0 and cmd.filename:
|
| 145 |
+
self._files_read.add(cmd.filename)
|
| 146 |
+
if cmd.type in ("echo_append", "sed") and exit_code == 0:
|
| 147 |
+
self._pipeline_runs_since_last_edit = 0
|
| 148 |
+
if cmd.type == "pipeline_run":
|
| 149 |
+
self._pipeline_runs_since_last_edit += 1
|
| 150 |
+
self._fs_snapshot_at_last_run = repr(self._filesystem)
|
| 151 |
+
|
| 152 |
+
if self._pipeline_status == "passed" or steps_remaining <= 0:
|
| 153 |
+
self._done = True
|
| 154 |
+
|
| 155 |
+
return PipelineObservation(
|
| 156 |
+
stdout=stdout,
|
| 157 |
+
exit_code=exit_code,
|
| 158 |
+
pipeline_status=self._pipeline_status,
|
| 159 |
+
steps_remaining=max(0, steps_remaining),
|
| 160 |
+
done=self._done,
|
| 161 |
+
reward=reward,
|
| 162 |
+
)
|
| 163 |
+
|
| 164 |
+
def state(self) -> PipelineState:
|
| 165 |
+
return PipelineState(
|
| 166 |
+
episode_id=self._episode_id,
|
| 167 |
+
task=self._task,
|
| 168 |
+
filesystem=dict(self._filesystem),
|
| 169 |
+
pipeline_status=self._pipeline_status,
|
| 170 |
+
step_count=self._step_count,
|
| 171 |
+
done=self._done,
|
| 172 |
+
total_reward=self._total_reward,
|
| 173 |
+
answer_key=self._answer_key,
|
| 174 |
+
milestones=sorted(self._milestones),
|
| 175 |
+
)
|
| 176 |
+
|
| 177 |
+
def _update_milestones(self, cmd) -> None:
|
| 178 |
+
"""Mark reward tiers unlocked by this step. Each tier fires once."""
|
| 179 |
+
if cmd.type == "pipeline_run":
|
| 180 |
+
self._milestones.add("investigated")
|
| 181 |
+
|
| 182 |
+
is_log_read = cmd.type == "pipeline_logs" or (
|
| 183 |
+
cmd.type == "cat" and cmd.filename == "logs/install.log"
|
| 184 |
+
)
|
| 185 |
+
if is_log_read and self._pipeline_status == "failed":
|
| 186 |
+
self._milestones.add("logs_read")
|
| 187 |
+
|
| 188 |
+
fix_files = set(self._answer_key.get("fixes", {}).keys())
|
| 189 |
+
if cmd.type == "cat" and cmd.filename in fix_files:
|
| 190 |
+
self._milestones.add("correct_file_located")
|
| 191 |
+
|
| 192 |
+
def _grade(self) -> tuple[float, bool]:
|
| 193 |
+
"""
|
| 194 |
+
Delegate to the unified grader. Same rule for every task difficulty.
|
| 195 |
+
Episode ends only when the pipeline has passed.
|
| 196 |
+
"""
|
| 197 |
+
score = grade_state(self.state())
|
| 198 |
+
done = self._pipeline_status == "passed"
|
| 199 |
+
return score, done
|
uv.lock
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
venv/bin/Activate.ps1
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<#
|
| 2 |
+
.Synopsis
|
| 3 |
+
Activate a Python virtual environment for the current PowerShell session.
|
| 4 |
+
|
| 5 |
+
.Description
|
| 6 |
+
Pushes the python executable for a virtual environment to the front of the
|
| 7 |
+
$Env:PATH environment variable and sets the prompt to signify that you are
|
| 8 |
+
in a Python virtual environment. Makes use of the command line switches as
|
| 9 |
+
well as the `pyvenv.cfg` file values present in the virtual environment.
|
| 10 |
+
|
| 11 |
+
.Parameter VenvDir
|
| 12 |
+
Path to the directory that contains the virtual environment to activate. The
|
| 13 |
+
default value for this is the parent of the directory that the Activate.ps1
|
| 14 |
+
script is located within.
|
| 15 |
+
|
| 16 |
+
.Parameter Prompt
|
| 17 |
+
The prompt prefix to display when this virtual environment is activated. By
|
| 18 |
+
default, this prompt is the name of the virtual environment folder (VenvDir)
|
| 19 |
+
surrounded by parentheses and followed by a single space (ie. '(.venv) ').
|
| 20 |
+
|
| 21 |
+
.Example
|
| 22 |
+
Activate.ps1
|
| 23 |
+
Activates the Python virtual environment that contains the Activate.ps1 script.
|
| 24 |
+
|
| 25 |
+
.Example
|
| 26 |
+
Activate.ps1 -Verbose
|
| 27 |
+
Activates the Python virtual environment that contains the Activate.ps1 script,
|
| 28 |
+
and shows extra information about the activation as it executes.
|
| 29 |
+
|
| 30 |
+
.Example
|
| 31 |
+
Activate.ps1 -VenvDir C:\Users\MyUser\Common\.venv
|
| 32 |
+
Activates the Python virtual environment located in the specified location.
|
| 33 |
+
|
| 34 |
+
.Example
|
| 35 |
+
Activate.ps1 -Prompt "MyPython"
|
| 36 |
+
Activates the Python virtual environment that contains the Activate.ps1 script,
|
| 37 |
+
and prefixes the current prompt with the specified string (surrounded in
|
| 38 |
+
parentheses) while the virtual environment is active.
|
| 39 |
+
|
| 40 |
+
.Notes
|
| 41 |
+
On Windows, it may be required to enable this Activate.ps1 script by setting the
|
| 42 |
+
execution policy for the user. You can do this by issuing the following PowerShell
|
| 43 |
+
command:
|
| 44 |
+
|
| 45 |
+
PS C:\> Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
|
| 46 |
+
|
| 47 |
+
For more information on Execution Policies:
|
| 48 |
+
https://go.microsoft.com/fwlink/?LinkID=135170
|
| 49 |
+
|
| 50 |
+
#>
|
| 51 |
+
Param(
|
| 52 |
+
[Parameter(Mandatory = $false)]
|
| 53 |
+
[String]
|
| 54 |
+
$VenvDir,
|
| 55 |
+
[Parameter(Mandatory = $false)]
|
| 56 |
+
[String]
|
| 57 |
+
$Prompt
|
| 58 |
+
)
|
| 59 |
+
|
| 60 |
+
<# Function declarations --------------------------------------------------- #>
|
| 61 |
+
|
| 62 |
+
<#
|
| 63 |
+
.Synopsis
|
| 64 |
+
Remove all shell session elements added by the Activate script, including the
|
| 65 |
+
addition of the virtual environment's Python executable from the beginning of
|
| 66 |
+
the PATH variable.
|
| 67 |
+
|
| 68 |
+
.Parameter NonDestructive
|
| 69 |
+
If present, do not remove this function from the global namespace for the
|
| 70 |
+
session.
|
| 71 |
+
|
| 72 |
+
#>
|
| 73 |
+
function global:deactivate ([switch]$NonDestructive) {
|
| 74 |
+
# Revert to original values
|
| 75 |
+
|
| 76 |
+
# The prior prompt:
|
| 77 |
+
if (Test-Path -Path Function:_OLD_VIRTUAL_PROMPT) {
|
| 78 |
+
Copy-Item -Path Function:_OLD_VIRTUAL_PROMPT -Destination Function:prompt
|
| 79 |
+
Remove-Item -Path Function:_OLD_VIRTUAL_PROMPT
|
| 80 |
+
}
|
| 81 |
+
|
| 82 |
+
# The prior PYTHONHOME:
|
| 83 |
+
if (Test-Path -Path Env:_OLD_VIRTUAL_PYTHONHOME) {
|
| 84 |
+
Copy-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME -Destination Env:PYTHONHOME
|
| 85 |
+
Remove-Item -Path Env:_OLD_VIRTUAL_PYTHONHOME
|
| 86 |
+
}
|
| 87 |
+
|
| 88 |
+
# The prior PATH:
|
| 89 |
+
if (Test-Path -Path Env:_OLD_VIRTUAL_PATH) {
|
| 90 |
+
Copy-Item -Path Env:_OLD_VIRTUAL_PATH -Destination Env:PATH
|
| 91 |
+
Remove-Item -Path Env:_OLD_VIRTUAL_PATH
|
| 92 |
+
}
|
| 93 |
+
|
| 94 |
+
# Just remove the VIRTUAL_ENV altogether:
|
| 95 |
+
if (Test-Path -Path Env:VIRTUAL_ENV) {
|
| 96 |
+
Remove-Item -Path env:VIRTUAL_ENV
|
| 97 |
+
}
|
| 98 |
+
|
| 99 |
+
# Just remove VIRTUAL_ENV_PROMPT altogether.
|
| 100 |
+
if (Test-Path -Path Env:VIRTUAL_ENV_PROMPT) {
|
| 101 |
+
Remove-Item -Path env:VIRTUAL_ENV_PROMPT
|
| 102 |
+
}
|
| 103 |
+
|
| 104 |
+
# Just remove the _PYTHON_VENV_PROMPT_PREFIX altogether:
|
| 105 |
+
if (Get-Variable -Name "_PYTHON_VENV_PROMPT_PREFIX" -ErrorAction SilentlyContinue) {
|
| 106 |
+
Remove-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Scope Global -Force
|
| 107 |
+
}
|
| 108 |
+
|
| 109 |
+
# Leave deactivate function in the global namespace if requested:
|
| 110 |
+
if (-not $NonDestructive) {
|
| 111 |
+
Remove-Item -Path function:deactivate
|
| 112 |
+
}
|
| 113 |
+
}
|
| 114 |
+
|
| 115 |
+
<#
|
| 116 |
+
.Description
|
| 117 |
+
Get-PyVenvConfig parses the values from the pyvenv.cfg file located in the
|
| 118 |
+
given folder, and returns them in a map.
|
| 119 |
+
|
| 120 |
+
For each line in the pyvenv.cfg file, if that line can be parsed into exactly
|
| 121 |
+
two strings separated by `=` (with any amount of whitespace surrounding the =)
|
| 122 |
+
then it is considered a `key = value` line. The left hand string is the key,
|
| 123 |
+
the right hand is the value.
|
| 124 |
+
|
| 125 |
+
If the value starts with a `'` or a `"` then the first and last character is
|
| 126 |
+
stripped from the value before being captured.
|
| 127 |
+
|
| 128 |
+
.Parameter ConfigDir
|
| 129 |
+
Path to the directory that contains the `pyvenv.cfg` file.
|
| 130 |
+
#>
|
| 131 |
+
function Get-PyVenvConfig(
|
| 132 |
+
[String]
|
| 133 |
+
$ConfigDir
|
| 134 |
+
) {
|
| 135 |
+
Write-Verbose "Given ConfigDir=$ConfigDir, obtain values in pyvenv.cfg"
|
| 136 |
+
|
| 137 |
+
# Ensure the file exists, and issue a warning if it doesn't (but still allow the function to continue).
|
| 138 |
+
$pyvenvConfigPath = Join-Path -Resolve -Path $ConfigDir -ChildPath 'pyvenv.cfg' -ErrorAction Continue
|
| 139 |
+
|
| 140 |
+
# An empty map will be returned if no config file is found.
|
| 141 |
+
$pyvenvConfig = @{ }
|
| 142 |
+
|
| 143 |
+
if ($pyvenvConfigPath) {
|
| 144 |
+
|
| 145 |
+
Write-Verbose "File exists, parse `key = value` lines"
|
| 146 |
+
$pyvenvConfigContent = Get-Content -Path $pyvenvConfigPath
|
| 147 |
+
|
| 148 |
+
$pyvenvConfigContent | ForEach-Object {
|
| 149 |
+
$keyval = $PSItem -split "\s*=\s*", 2
|
| 150 |
+
if ($keyval[0] -and $keyval[1]) {
|
| 151 |
+
$val = $keyval[1]
|
| 152 |
+
|
| 153 |
+
# Remove extraneous quotations around a string value.
|
| 154 |
+
if ("'""".Contains($val.Substring(0, 1))) {
|
| 155 |
+
$val = $val.Substring(1, $val.Length - 2)
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
$pyvenvConfig[$keyval[0]] = $val
|
| 159 |
+
Write-Verbose "Adding Key: '$($keyval[0])'='$val'"
|
| 160 |
+
}
|
| 161 |
+
}
|
| 162 |
+
}
|
| 163 |
+
return $pyvenvConfig
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
|
| 167 |
+
<# Begin Activate script --------------------------------------------------- #>
|
| 168 |
+
|
| 169 |
+
# Determine the containing directory of this script
|
| 170 |
+
$VenvExecPath = Split-Path -Parent $MyInvocation.MyCommand.Definition
|
| 171 |
+
$VenvExecDir = Get-Item -Path $VenvExecPath
|
| 172 |
+
|
| 173 |
+
Write-Verbose "Activation script is located in path: '$VenvExecPath'"
|
| 174 |
+
Write-Verbose "VenvExecDir Fullname: '$($VenvExecDir.FullName)"
|
| 175 |
+
Write-Verbose "VenvExecDir Name: '$($VenvExecDir.Name)"
|
| 176 |
+
|
| 177 |
+
# Set values required in priority: CmdLine, ConfigFile, Default
|
| 178 |
+
# First, get the location of the virtual environment, it might not be
|
| 179 |
+
# VenvExecDir if specified on the command line.
|
| 180 |
+
if ($VenvDir) {
|
| 181 |
+
Write-Verbose "VenvDir given as parameter, using '$VenvDir' to determine values"
|
| 182 |
+
}
|
| 183 |
+
else {
|
| 184 |
+
Write-Verbose "VenvDir not given as a parameter, using parent directory name as VenvDir."
|
| 185 |
+
$VenvDir = $VenvExecDir.Parent.FullName.TrimEnd("\\/")
|
| 186 |
+
Write-Verbose "VenvDir=$VenvDir"
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
# Next, read the `pyvenv.cfg` file to determine any required value such
|
| 190 |
+
# as `prompt`.
|
| 191 |
+
$pyvenvCfg = Get-PyVenvConfig -ConfigDir $VenvDir
|
| 192 |
+
|
| 193 |
+
# Next, set the prompt from the command line, or the config file, or
|
| 194 |
+
# just use the name of the virtual environment folder.
|
| 195 |
+
if ($Prompt) {
|
| 196 |
+
Write-Verbose "Prompt specified as argument, using '$Prompt'"
|
| 197 |
+
}
|
| 198 |
+
else {
|
| 199 |
+
Write-Verbose "Prompt not specified as argument to script, checking pyvenv.cfg value"
|
| 200 |
+
if ($pyvenvCfg -and $pyvenvCfg['prompt']) {
|
| 201 |
+
Write-Verbose " Setting based on value in pyvenv.cfg='$($pyvenvCfg['prompt'])'"
|
| 202 |
+
$Prompt = $pyvenvCfg['prompt'];
|
| 203 |
+
}
|
| 204 |
+
else {
|
| 205 |
+
Write-Verbose " Setting prompt based on parent's directory's name. (Is the directory name passed to venv module when creating the virtual environment)"
|
| 206 |
+
Write-Verbose " Got leaf-name of $VenvDir='$(Split-Path -Path $venvDir -Leaf)'"
|
| 207 |
+
$Prompt = Split-Path -Path $venvDir -Leaf
|
| 208 |
+
}
|
| 209 |
+
}
|
| 210 |
+
|
| 211 |
+
Write-Verbose "Prompt = '$Prompt'"
|
| 212 |
+
Write-Verbose "VenvDir='$VenvDir'"
|
| 213 |
+
|
| 214 |
+
# Deactivate any currently active virtual environment, but leave the
|
| 215 |
+
# deactivate function in place.
|
| 216 |
+
deactivate -nondestructive
|
| 217 |
+
|
| 218 |
+
# Now set the environment variable VIRTUAL_ENV, used by many tools to determine
|
| 219 |
+
# that there is an activated venv.
|
| 220 |
+
$env:VIRTUAL_ENV = $VenvDir
|
| 221 |
+
|
| 222 |
+
if (-not $Env:VIRTUAL_ENV_DISABLE_PROMPT) {
|
| 223 |
+
|
| 224 |
+
Write-Verbose "Setting prompt to '$Prompt'"
|
| 225 |
+
|
| 226 |
+
# Set the prompt to include the env name
|
| 227 |
+
# Make sure _OLD_VIRTUAL_PROMPT is global
|
| 228 |
+
function global:_OLD_VIRTUAL_PROMPT { "" }
|
| 229 |
+
Copy-Item -Path function:prompt -Destination function:_OLD_VIRTUAL_PROMPT
|
| 230 |
+
New-Variable -Name _PYTHON_VENV_PROMPT_PREFIX -Description "Python virtual environment prompt prefix" -Scope Global -Option ReadOnly -Visibility Public -Value $Prompt
|
| 231 |
+
|
| 232 |
+
function global:prompt {
|
| 233 |
+
Write-Host -NoNewline -ForegroundColor Green "($_PYTHON_VENV_PROMPT_PREFIX) "
|
| 234 |
+
_OLD_VIRTUAL_PROMPT
|
| 235 |
+
}
|
| 236 |
+
$env:VIRTUAL_ENV_PROMPT = $Prompt
|
| 237 |
+
}
|
| 238 |
+
|
| 239 |
+
# Clear PYTHONHOME
|
| 240 |
+
if (Test-Path -Path Env:PYTHONHOME) {
|
| 241 |
+
Copy-Item -Path Env:PYTHONHOME -Destination Env:_OLD_VIRTUAL_PYTHONHOME
|
| 242 |
+
Remove-Item -Path Env:PYTHONHOME
|
| 243 |
+
}
|
| 244 |
+
|
| 245 |
+
# Add the venv to the PATH
|
| 246 |
+
Copy-Item -Path Env:PATH -Destination Env:_OLD_VIRTUAL_PATH
|
| 247 |
+
$Env:PATH = "$VenvExecDir$([System.IO.Path]::PathSeparator)$Env:PATH"
|
venv/bin/activate
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file must be used with "source bin/activate" *from bash*
|
| 2 |
+
# You cannot run it directly
|
| 3 |
+
|
| 4 |
+
deactivate () {
|
| 5 |
+
# reset old environment variables
|
| 6 |
+
if [ -n "${_OLD_VIRTUAL_PATH:-}" ] ; then
|
| 7 |
+
PATH="${_OLD_VIRTUAL_PATH:-}"
|
| 8 |
+
export PATH
|
| 9 |
+
unset _OLD_VIRTUAL_PATH
|
| 10 |
+
fi
|
| 11 |
+
if [ -n "${_OLD_VIRTUAL_PYTHONHOME:-}" ] ; then
|
| 12 |
+
PYTHONHOME="${_OLD_VIRTUAL_PYTHONHOME:-}"
|
| 13 |
+
export PYTHONHOME
|
| 14 |
+
unset _OLD_VIRTUAL_PYTHONHOME
|
| 15 |
+
fi
|
| 16 |
+
|
| 17 |
+
# Call hash to forget past locations. Without forgetting
|
| 18 |
+
# past locations the $PATH changes we made may not be respected.
|
| 19 |
+
# See "man bash" for more details. hash is usually a builtin of your shell
|
| 20 |
+
hash -r 2> /dev/null
|
| 21 |
+
|
| 22 |
+
if [ -n "${_OLD_VIRTUAL_PS1:-}" ] ; then
|
| 23 |
+
PS1="${_OLD_VIRTUAL_PS1:-}"
|
| 24 |
+
export PS1
|
| 25 |
+
unset _OLD_VIRTUAL_PS1
|
| 26 |
+
fi
|
| 27 |
+
|
| 28 |
+
unset VIRTUAL_ENV
|
| 29 |
+
unset VIRTUAL_ENV_PROMPT
|
| 30 |
+
if [ ! "${1:-}" = "nondestructive" ] ; then
|
| 31 |
+
# Self destruct!
|
| 32 |
+
unset -f deactivate
|
| 33 |
+
fi
|
| 34 |
+
}
|
| 35 |
+
|
| 36 |
+
# unset irrelevant variables
|
| 37 |
+
deactivate nondestructive
|
| 38 |
+
|
| 39 |
+
# on Windows, a path can contain colons and backslashes and has to be converted:
|
| 40 |
+
if [ "${OSTYPE:-}" = "cygwin" ] || [ "${OSTYPE:-}" = "msys" ] ; then
|
| 41 |
+
# transform D:\path\to\venv to /d/path/to/venv on MSYS
|
| 42 |
+
# and to /cygdrive/d/path/to/venv on Cygwin
|
| 43 |
+
export VIRTUAL_ENV=$(cygpath /Users/samratrm/workspace/CI_CD_Doctor/venv)
|
| 44 |
+
else
|
| 45 |
+
# use the path as-is
|
| 46 |
+
export VIRTUAL_ENV=/Users/samratrm/workspace/CI_CD_Doctor/venv
|
| 47 |
+
fi
|
| 48 |
+
|
| 49 |
+
_OLD_VIRTUAL_PATH="$PATH"
|
| 50 |
+
PATH="$VIRTUAL_ENV/"bin":$PATH"
|
| 51 |
+
export PATH
|
| 52 |
+
|
| 53 |
+
# unset PYTHONHOME if set
|
| 54 |
+
# this will fail if PYTHONHOME is set to the empty string (which is bad anyway)
|
| 55 |
+
# could use `if (set -u; : $PYTHONHOME) ;` in bash
|
| 56 |
+
if [ -n "${PYTHONHOME:-}" ] ; then
|
| 57 |
+
_OLD_VIRTUAL_PYTHONHOME="${PYTHONHOME:-}"
|
| 58 |
+
unset PYTHONHOME
|
| 59 |
+
fi
|
| 60 |
+
|
| 61 |
+
if [ -z "${VIRTUAL_ENV_DISABLE_PROMPT:-}" ] ; then
|
| 62 |
+
_OLD_VIRTUAL_PS1="${PS1:-}"
|
| 63 |
+
PS1='(venv) '"${PS1:-}"
|
| 64 |
+
export PS1
|
| 65 |
+
VIRTUAL_ENV_PROMPT='(venv) '
|
| 66 |
+
export VIRTUAL_ENV_PROMPT
|
| 67 |
+
fi
|
| 68 |
+
|
| 69 |
+
# Call hash to forget past commands. Without forgetting
|
| 70 |
+
# past commands the $PATH changes we made may not be respected
|
| 71 |
+
hash -r 2> /dev/null
|
venv/bin/activate.csh
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file must be used with "source bin/activate.csh" *from csh*.
|
| 2 |
+
# You cannot run it directly.
|
| 3 |
+
|
| 4 |
+
# Created by Davide Di Blasi <davidedb@gmail.com>.
|
| 5 |
+
# Ported to Python 3.3 venv by Andrew Svetlov <andrew.svetlov@gmail.com>
|
| 6 |
+
|
| 7 |
+
alias deactivate 'test $?_OLD_VIRTUAL_PATH != 0 && setenv PATH "$_OLD_VIRTUAL_PATH" && unset _OLD_VIRTUAL_PATH; rehash; test $?_OLD_VIRTUAL_PROMPT != 0 && set prompt="$_OLD_VIRTUAL_PROMPT" && unset _OLD_VIRTUAL_PROMPT; unsetenv VIRTUAL_ENV; unsetenv VIRTUAL_ENV_PROMPT; test "\!:*" != "nondestructive" && unalias deactivate'
|
| 8 |
+
|
| 9 |
+
# Unset irrelevant variables.
|
| 10 |
+
deactivate nondestructive
|
| 11 |
+
|
| 12 |
+
setenv VIRTUAL_ENV /Users/samratrm/workspace/CI_CD_Doctor/venv
|
| 13 |
+
|
| 14 |
+
set _OLD_VIRTUAL_PATH="$PATH"
|
| 15 |
+
setenv PATH "$VIRTUAL_ENV/"bin":$PATH"
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
set _OLD_VIRTUAL_PROMPT="$prompt"
|
| 19 |
+
|
| 20 |
+
if (! "$?VIRTUAL_ENV_DISABLE_PROMPT") then
|
| 21 |
+
set prompt = '(venv) '"$prompt"
|
| 22 |
+
setenv VIRTUAL_ENV_PROMPT '(venv) '
|
| 23 |
+
endif
|
| 24 |
+
|
| 25 |
+
alias pydoc python -m pydoc
|
| 26 |
+
|
| 27 |
+
rehash
|
venv/bin/activate.fish
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# This file must be used with "source <venv>/bin/activate.fish" *from fish*
|
| 2 |
+
# (https://fishshell.com/). You cannot run it directly.
|
| 3 |
+
|
| 4 |
+
function deactivate -d "Exit virtual environment and return to normal shell environment"
|
| 5 |
+
# reset old environment variables
|
| 6 |
+
if test -n "$_OLD_VIRTUAL_PATH"
|
| 7 |
+
set -gx PATH $_OLD_VIRTUAL_PATH
|
| 8 |
+
set -e _OLD_VIRTUAL_PATH
|
| 9 |
+
end
|
| 10 |
+
if test -n "$_OLD_VIRTUAL_PYTHONHOME"
|
| 11 |
+
set -gx PYTHONHOME $_OLD_VIRTUAL_PYTHONHOME
|
| 12 |
+
set -e _OLD_VIRTUAL_PYTHONHOME
|
| 13 |
+
end
|
| 14 |
+
|
| 15 |
+
if test -n "$_OLD_FISH_PROMPT_OVERRIDE"
|
| 16 |
+
set -e _OLD_FISH_PROMPT_OVERRIDE
|
| 17 |
+
# prevents error when using nested fish instances (Issue #93858)
|
| 18 |
+
if functions -q _old_fish_prompt
|
| 19 |
+
functions -e fish_prompt
|
| 20 |
+
functions -c _old_fish_prompt fish_prompt
|
| 21 |
+
functions -e _old_fish_prompt
|
| 22 |
+
end
|
| 23 |
+
end
|
| 24 |
+
|
| 25 |
+
set -e VIRTUAL_ENV
|
| 26 |
+
set -e VIRTUAL_ENV_PROMPT
|
| 27 |
+
if test "$argv[1]" != "nondestructive"
|
| 28 |
+
# Self-destruct!
|
| 29 |
+
functions -e deactivate
|
| 30 |
+
end
|
| 31 |
+
end
|
| 32 |
+
|
| 33 |
+
# Unset irrelevant variables.
|
| 34 |
+
deactivate nondestructive
|
| 35 |
+
|
| 36 |
+
set -gx VIRTUAL_ENV /Users/samratrm/workspace/CI_CD_Doctor/venv
|
| 37 |
+
|
| 38 |
+
set -gx _OLD_VIRTUAL_PATH $PATH
|
| 39 |
+
set -gx PATH "$VIRTUAL_ENV/"bin $PATH
|
| 40 |
+
|
| 41 |
+
# Unset PYTHONHOME if set.
|
| 42 |
+
if set -q PYTHONHOME
|
| 43 |
+
set -gx _OLD_VIRTUAL_PYTHONHOME $PYTHONHOME
|
| 44 |
+
set -e PYTHONHOME
|
| 45 |
+
end
|
| 46 |
+
|
| 47 |
+
if test -z "$VIRTUAL_ENV_DISABLE_PROMPT"
|
| 48 |
+
# fish uses a function instead of an env var to generate the prompt.
|
| 49 |
+
|
| 50 |
+
# Save the current fish_prompt function as the function _old_fish_prompt.
|
| 51 |
+
functions -c fish_prompt _old_fish_prompt
|
| 52 |
+
|
| 53 |
+
# With the original prompt function renamed, we can override with our own.
|
| 54 |
+
function fish_prompt
|
| 55 |
+
# Save the return status of the last command.
|
| 56 |
+
set -l old_status $status
|
| 57 |
+
|
| 58 |
+
# Output the venv prompt; color taken from the blue of the Python logo.
|
| 59 |
+
printf "%s%s%s" (set_color 4B8BBE) '(venv) ' (set_color normal)
|
| 60 |
+
|
| 61 |
+
# Restore the return status of the previous command.
|
| 62 |
+
echo "exit $old_status" | .
|
| 63 |
+
# Output the original/"old" prompt.
|
| 64 |
+
_old_fish_prompt
|
| 65 |
+
end
|
| 66 |
+
|
| 67 |
+
set -gx _OLD_FISH_PROMPT_OVERRIDE "$VIRTUAL_ENV"
|
| 68 |
+
set -gx VIRTUAL_ENV_PROMPT '(venv) '
|
| 69 |
+
end
|
venv/bin/pip
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/Users/samratrm/workspace/CI_CD_Doctor/venv/bin/python
|
| 2 |
+
# -*- coding: utf-8 -*-
|
| 3 |
+
import re
|
| 4 |
+
import sys
|
| 5 |
+
from pip._internal.cli.main import main
|
| 6 |
+
if __name__ == '__main__':
|
| 7 |
+
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
| 8 |
+
sys.exit(main())
|
venv/bin/pip3
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/Users/samratrm/workspace/CI_CD_Doctor/venv/bin/python
|
| 2 |
+
# -*- coding: utf-8 -*-
|
| 3 |
+
import re
|
| 4 |
+
import sys
|
| 5 |
+
from pip._internal.cli.main import main
|
| 6 |
+
if __name__ == '__main__':
|
| 7 |
+
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
| 8 |
+
sys.exit(main())
|
venv/bin/pip3.12
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#!/Users/samratrm/workspace/CI_CD_Doctor/venv/bin/python
|
| 2 |
+
# -*- coding: utf-8 -*-
|
| 3 |
+
import re
|
| 4 |
+
import sys
|
| 5 |
+
from pip._internal.cli.main import main
|
| 6 |
+
if __name__ == '__main__':
|
| 7 |
+
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
|
| 8 |
+
sys.exit(main())
|
venv/bin/python
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b379bc0cd9cb75642c74b3e1ca139631b9414cc8af307feb70bf5652d14e41d7
|
| 3 |
+
size 6592880
|
venv/bin/python3
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b379bc0cd9cb75642c74b3e1ca139631b9414cc8af307feb70bf5652d14e41d7
|
| 3 |
+
size 6592880
|
venv/bin/python3.12
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:b379bc0cd9cb75642c74b3e1ca139631b9414cc8af307feb70bf5652d14e41d7
|
| 3 |
+
size 6592880
|
venv/lib/python3.12/site-packages/pip-24.3.1.dist-info/AUTHORS.txt
ADDED
|
@@ -0,0 +1,799 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
@Switch01
|
| 2 |
+
A_Rog
|
| 3 |
+
Aakanksha Agrawal
|
| 4 |
+
Abhinav Sagar
|
| 5 |
+
ABHYUDAY PRATAP SINGH
|
| 6 |
+
abs51295
|
| 7 |
+
AceGentile
|
| 8 |
+
Adam Chainz
|
| 9 |
+
Adam Tse
|
| 10 |
+
Adam Wentz
|
| 11 |
+
admin
|
| 12 |
+
Adolfo Ochagavía
|
| 13 |
+
Adrien Morison
|
| 14 |
+
Agus
|
| 15 |
+
ahayrapetyan
|
| 16 |
+
Ahilya
|
| 17 |
+
AinsworthK
|
| 18 |
+
Akash Srivastava
|
| 19 |
+
Alan Yee
|
| 20 |
+
Albert Tugushev
|
| 21 |
+
Albert-Guan
|
| 22 |
+
albertg
|
| 23 |
+
Alberto Sottile
|
| 24 |
+
Aleks Bunin
|
| 25 |
+
Ales Erjavec
|
| 26 |
+
Alethea Flowers
|
| 27 |
+
Alex Gaynor
|
| 28 |
+
Alex Grönholm
|
| 29 |
+
Alex Hedges
|
| 30 |
+
Alex Loosley
|
| 31 |
+
Alex Morega
|
| 32 |
+
Alex Stachowiak
|
| 33 |
+
Alexander Shtyrov
|
| 34 |
+
Alexandre Conrad
|
| 35 |
+
Alexey Popravka
|
| 36 |
+
Aleš Erjavec
|
| 37 |
+
Alli
|
| 38 |
+
Ami Fischman
|
| 39 |
+
Ananya Maiti
|
| 40 |
+
Anatoly Techtonik
|
| 41 |
+
Anders Kaseorg
|
| 42 |
+
Andre Aguiar
|
| 43 |
+
Andreas Lutro
|
| 44 |
+
Andrei Geacar
|
| 45 |
+
Andrew Gaul
|
| 46 |
+
Andrew Shymanel
|
| 47 |
+
Andrey Bienkowski
|
| 48 |
+
Andrey Bulgakov
|
| 49 |
+
Andrés Delfino
|
| 50 |
+
Andy Freeland
|
| 51 |
+
Andy Kluger
|
| 52 |
+
Ani Hayrapetyan
|
| 53 |
+
Aniruddha Basak
|
| 54 |
+
Anish Tambe
|
| 55 |
+
Anrs Hu
|
| 56 |
+
Anthony Sottile
|
| 57 |
+
Antoine Musso
|
| 58 |
+
Anton Ovchinnikov
|
| 59 |
+
Anton Patrushev
|
| 60 |
+
Anton Zelenov
|
| 61 |
+
Antonio Alvarado Hernandez
|
| 62 |
+
Antony Lee
|
| 63 |
+
Antti Kaihola
|
| 64 |
+
Anubhav Patel
|
| 65 |
+
Anudit Nagar
|
| 66 |
+
Anuj Godase
|
| 67 |
+
AQNOUCH Mohammed
|
| 68 |
+
AraHaan
|
| 69 |
+
arena
|
| 70 |
+
arenasys
|
| 71 |
+
Arindam Choudhury
|
| 72 |
+
Armin Ronacher
|
| 73 |
+
Arnon Yaari
|
| 74 |
+
Artem
|
| 75 |
+
Arun Babu Neelicattu
|
| 76 |
+
Ashley Manton
|
| 77 |
+
Ashwin Ramaswami
|
| 78 |
+
atse
|
| 79 |
+
Atsushi Odagiri
|
| 80 |
+
Avinash Karhana
|
| 81 |
+
Avner Cohen
|
| 82 |
+
Awit (Ah-Wit) Ghirmai
|
| 83 |
+
Baptiste Mispelon
|
| 84 |
+
Barney Gale
|
| 85 |
+
barneygale
|
| 86 |
+
Bartek Ogryczak
|
| 87 |
+
Bastian Venthur
|
| 88 |
+
Ben Bodenmiller
|
| 89 |
+
Ben Darnell
|
| 90 |
+
Ben Hoyt
|
| 91 |
+
Ben Mares
|
| 92 |
+
Ben Rosser
|
| 93 |
+
Bence Nagy
|
| 94 |
+
Benjamin Peterson
|
| 95 |
+
Benjamin VanEvery
|
| 96 |
+
Benoit Pierre
|
| 97 |
+
Berker Peksag
|
| 98 |
+
Bernard
|
| 99 |
+
Bernard Tyers
|
| 100 |
+
Bernardo B. Marques
|
| 101 |
+
Bernhard M. Wiedemann
|
| 102 |
+
Bertil Hatt
|
| 103 |
+
Bhavam Vidyarthi
|
| 104 |
+
Blazej Michalik
|
| 105 |
+
Bogdan Opanchuk
|
| 106 |
+
BorisZZZ
|
| 107 |
+
Brad Erickson
|
| 108 |
+
Bradley Ayers
|
| 109 |
+
Branch Vincent
|
| 110 |
+
Brandon L. Reiss
|
| 111 |
+
Brandt Bucher
|
| 112 |
+
Brannon Dorsey
|
| 113 |
+
Brett Randall
|
| 114 |
+
Brett Rosen
|
| 115 |
+
Brian Cristante
|
| 116 |
+
Brian Rosner
|
| 117 |
+
briantracy
|
| 118 |
+
BrownTruck
|
| 119 |
+
Bruno Oliveira
|
| 120 |
+
Bruno Renié
|
| 121 |
+
Bruno S
|
| 122 |
+
Bstrdsmkr
|
| 123 |
+
Buck Golemon
|
| 124 |
+
burrows
|
| 125 |
+
Bussonnier Matthias
|
| 126 |
+
bwoodsend
|
| 127 |
+
c22
|
| 128 |
+
Caleb Martinez
|
| 129 |
+
Calvin Smith
|
| 130 |
+
Carl Meyer
|
| 131 |
+
Carlos Liam
|
| 132 |
+
Carol Willing
|
| 133 |
+
Carter Thayer
|
| 134 |
+
Cass
|
| 135 |
+
Chandrasekhar Atina
|
| 136 |
+
Charlie Marsh
|
| 137 |
+
Chih-Hsuan Yen
|
| 138 |
+
Chris Brinker
|
| 139 |
+
Chris Hunt
|
| 140 |
+
Chris Jerdonek
|
| 141 |
+
Chris Kuehl
|
| 142 |
+
Chris Markiewicz
|
| 143 |
+
Chris McDonough
|
| 144 |
+
Chris Pawley
|
| 145 |
+
Chris Pryer
|
| 146 |
+
Chris Wolfe
|
| 147 |
+
Christian Clauss
|
| 148 |
+
Christian Heimes
|
| 149 |
+
Christian Oudard
|
| 150 |
+
Christoph Reiter
|
| 151 |
+
Christopher Hunt
|
| 152 |
+
Christopher Snyder
|
| 153 |
+
chrysle
|
| 154 |
+
cjc7373
|
| 155 |
+
Clark Boylan
|
| 156 |
+
Claudio Jolowicz
|
| 157 |
+
Clay McClure
|
| 158 |
+
Cody
|
| 159 |
+
Cody Soyland
|
| 160 |
+
Colin Watson
|
| 161 |
+
Collin Anderson
|
| 162 |
+
Connor Osborn
|
| 163 |
+
Cooper Lees
|
| 164 |
+
Cooper Ry Lees
|
| 165 |
+
Cory Benfield
|
| 166 |
+
Cory Wright
|
| 167 |
+
Craig Kerstiens
|
| 168 |
+
Cristian Sorinel
|
| 169 |
+
Cristina
|
| 170 |
+
Cristina Muñoz
|
| 171 |
+
ctg123
|
| 172 |
+
Curtis Doty
|
| 173 |
+
cytolentino
|
| 174 |
+
Daan De Meyer
|
| 175 |
+
Dale
|
| 176 |
+
Damian
|
| 177 |
+
Damian Quiroga
|
| 178 |
+
Damian Shaw
|
| 179 |
+
Dan Black
|
| 180 |
+
Dan Savilonis
|
| 181 |
+
Dan Sully
|
| 182 |
+
Dane Hillard
|
| 183 |
+
daniel
|
| 184 |
+
Daniel Collins
|
| 185 |
+
Daniel Hahler
|
| 186 |
+
Daniel Holth
|
| 187 |
+
Daniel Jost
|
| 188 |
+
Daniel Katz
|
| 189 |
+
Daniel Shaulov
|
| 190 |
+
Daniele Esposti
|
| 191 |
+
Daniele Nicolodi
|
| 192 |
+
Daniele Procida
|
| 193 |
+
Daniil Konovalenko
|
| 194 |
+
Danny Hermes
|
| 195 |
+
Danny McClanahan
|
| 196 |
+
Darren Kavanagh
|
| 197 |
+
Dav Clark
|
| 198 |
+
Dave Abrahams
|
| 199 |
+
Dave Jones
|
| 200 |
+
David Aguilar
|
| 201 |
+
David Black
|
| 202 |
+
David Bordeynik
|
| 203 |
+
David Caro
|
| 204 |
+
David D Lowe
|
| 205 |
+
David Evans
|
| 206 |
+
David Hewitt
|
| 207 |
+
David Linke
|
| 208 |
+
David Poggi
|
| 209 |
+
David Poznik
|
| 210 |
+
David Pursehouse
|
| 211 |
+
David Runge
|
| 212 |
+
David Tucker
|
| 213 |
+
David Wales
|
| 214 |
+
Davidovich
|
| 215 |
+
ddelange
|
| 216 |
+
Deepak Sharma
|
| 217 |
+
Deepyaman Datta
|
| 218 |
+
Denise Yu
|
| 219 |
+
dependabot[bot]
|
| 220 |
+
derwolfe
|
| 221 |
+
Desetude
|
| 222 |
+
Devesh Kumar Singh
|
| 223 |
+
devsagul
|
| 224 |
+
Diego Caraballo
|
| 225 |
+
Diego Ramirez
|
| 226 |
+
DiegoCaraballo
|
| 227 |
+
Dimitri Merejkowsky
|
| 228 |
+
Dimitri Papadopoulos
|
| 229 |
+
Dimitri Papadopoulos Orfanos
|
| 230 |
+
Dirk Stolle
|
| 231 |
+
Dmitry Gladkov
|
| 232 |
+
Dmitry Volodin
|
| 233 |
+
Domen Kožar
|
| 234 |
+
Dominic Davis-Foster
|
| 235 |
+
Donald Stufft
|
| 236 |
+
Dongweiming
|
| 237 |
+
doron zarhi
|
| 238 |
+
Dos Moonen
|
| 239 |
+
Douglas Thor
|
| 240 |
+
DrFeathers
|
| 241 |
+
Dustin Ingram
|
| 242 |
+
Dustin Rodrigues
|
| 243 |
+
Dwayne Bailey
|
| 244 |
+
Ed Morley
|
| 245 |
+
Edgar Ramírez
|
| 246 |
+
Edgar Ramírez Mondragón
|
| 247 |
+
Ee Durbin
|
| 248 |
+
Efflam Lemaillet
|
| 249 |
+
efflamlemaillet
|
| 250 |
+
Eitan Adler
|
| 251 |
+
ekristina
|
| 252 |
+
elainechan
|
| 253 |
+
Eli Schwartz
|
| 254 |
+
Elisha Hollander
|
| 255 |
+
Ellen Marie Dash
|
| 256 |
+
Emil Burzo
|
| 257 |
+
Emil Styrke
|
| 258 |
+
Emmanuel Arias
|
| 259 |
+
Endoh Takanao
|
| 260 |
+
enoch
|
| 261 |
+
Erdinc Mutlu
|
| 262 |
+
Eric Cousineau
|
| 263 |
+
Eric Gillingham
|
| 264 |
+
Eric Hanchrow
|
| 265 |
+
Eric Hopper
|
| 266 |
+
Erik M. Bray
|
| 267 |
+
Erik Rose
|
| 268 |
+
Erwin Janssen
|
| 269 |
+
Eugene Vereshchagin
|
| 270 |
+
everdimension
|
| 271 |
+
Federico
|
| 272 |
+
Felipe Peter
|
| 273 |
+
Felix Yan
|
| 274 |
+
fiber-space
|
| 275 |
+
Filip Kokosiński
|
| 276 |
+
Filipe Laíns
|
| 277 |
+
Finn Womack
|
| 278 |
+
finnagin
|
| 279 |
+
Flavio Amurrio
|
| 280 |
+
Florian Briand
|
| 281 |
+
Florian Rathgeber
|
| 282 |
+
Francesco
|
| 283 |
+
Francesco Montesano
|
| 284 |
+
Fredrik Orderud
|
| 285 |
+
Frost Ming
|
| 286 |
+
Gabriel Curio
|
| 287 |
+
Gabriel de Perthuis
|
| 288 |
+
Garry Polley
|
| 289 |
+
gavin
|
| 290 |
+
gdanielson
|
| 291 |
+
Geoffrey Sneddon
|
| 292 |
+
George Song
|
| 293 |
+
Georgi Valkov
|
| 294 |
+
Georgy Pchelkin
|
| 295 |
+
ghost
|
| 296 |
+
Giftlin Rajaiah
|
| 297 |
+
gizmoguy1
|
| 298 |
+
gkdoc
|
| 299 |
+
Godefroid Chapelle
|
| 300 |
+
Gopinath M
|
| 301 |
+
GOTO Hayato
|
| 302 |
+
gousaiyang
|
| 303 |
+
gpiks
|
| 304 |
+
Greg Roodt
|
| 305 |
+
Greg Ward
|
| 306 |
+
Guilherme Espada
|
| 307 |
+
Guillaume Seguin
|
| 308 |
+
gutsytechster
|
| 309 |
+
Guy Rozendorn
|
| 310 |
+
Guy Tuval
|
| 311 |
+
gzpan123
|
| 312 |
+
Hanjun Kim
|
| 313 |
+
Hari Charan
|
| 314 |
+
Harsh Vardhan
|
| 315 |
+
harupy
|
| 316 |
+
Harutaka Kawamura
|
| 317 |
+
hauntsaninja
|
| 318 |
+
Henrich Hartzer
|
| 319 |
+
Henry Schreiner
|
| 320 |
+
Herbert Pfennig
|
| 321 |
+
Holly Stotelmyer
|
| 322 |
+
Honnix
|
| 323 |
+
Hsiaoming Yang
|
| 324 |
+
Hugo Lopes Tavares
|
| 325 |
+
Hugo van Kemenade
|
| 326 |
+
Hugues Bruant
|
| 327 |
+
Hynek Schlawack
|
| 328 |
+
Ian Bicking
|
| 329 |
+
Ian Cordasco
|
| 330 |
+
Ian Lee
|
| 331 |
+
Ian Stapleton Cordasco
|
| 332 |
+
Ian Wienand
|
| 333 |
+
Igor Kuzmitshov
|
| 334 |
+
Igor Sobreira
|
| 335 |
+
Ikko Ashimine
|
| 336 |
+
Ilan Schnell
|
| 337 |
+
Illia Volochii
|
| 338 |
+
Ilya Baryshev
|
| 339 |
+
Inada Naoki
|
| 340 |
+
Ionel Cristian Mărieș
|
| 341 |
+
Ionel Maries Cristian
|
| 342 |
+
Itamar Turner-Trauring
|
| 343 |
+
Ivan Pozdeev
|
| 344 |
+
J. Nick Koston
|
| 345 |
+
Jacob Kim
|
| 346 |
+
Jacob Walls
|
| 347 |
+
Jaime Sanz
|
| 348 |
+
jakirkham
|
| 349 |
+
Jakub Kuczys
|
| 350 |
+
Jakub Stasiak
|
| 351 |
+
Jakub Vysoky
|
| 352 |
+
Jakub Wilk
|
| 353 |
+
James Cleveland
|
| 354 |
+
James Curtin
|
| 355 |
+
James Firth
|
| 356 |
+
James Gerity
|
| 357 |
+
James Polley
|
| 358 |
+
Jan Pokorný
|
| 359 |
+
Jannis Leidel
|
| 360 |
+
Jarek Potiuk
|
| 361 |
+
jarondl
|
| 362 |
+
Jason Curtis
|
| 363 |
+
Jason R. Coombs
|
| 364 |
+
JasonMo
|
| 365 |
+
JasonMo1
|
| 366 |
+
Jay Graves
|
| 367 |
+
Jean Abou Samra
|
| 368 |
+
Jean-Christophe Fillion-Robin
|
| 369 |
+
Jeff Barber
|
| 370 |
+
Jeff Dairiki
|
| 371 |
+
Jeff Widman
|
| 372 |
+
Jelmer Vernooij
|
| 373 |
+
jenix21
|
| 374 |
+
Jeremy Fleischman
|
| 375 |
+
Jeremy Stanley
|
| 376 |
+
Jeremy Zafran
|
| 377 |
+
Jesse Rittner
|
| 378 |
+
Jiashuo Li
|
| 379 |
+
Jim Fisher
|
| 380 |
+
Jim Garrison
|
| 381 |
+
Jinzhe Zeng
|
| 382 |
+
Jiun Bae
|
| 383 |
+
Jivan Amara
|
| 384 |
+
Joe Bylund
|
| 385 |
+
Joe Michelini
|
| 386 |
+
John Paton
|
| 387 |
+
John Sirois
|
| 388 |
+
John T. Wodder II
|
| 389 |
+
John-Scott Atlakson
|
| 390 |
+
johnthagen
|
| 391 |
+
Jon Banafato
|
| 392 |
+
Jon Dufresne
|
| 393 |
+
Jon Parise
|
| 394 |
+
Jonas Nockert
|
| 395 |
+
Jonathan Herbert
|
| 396 |
+
Joonatan Partanen
|
| 397 |
+
Joost Molenaar
|
| 398 |
+
Jorge Niedbalski
|
| 399 |
+
Joseph Bylund
|
| 400 |
+
Joseph Long
|
| 401 |
+
Josh Bronson
|
| 402 |
+
Josh Cannon
|
| 403 |
+
Josh Hansen
|
| 404 |
+
Josh Schneier
|
| 405 |
+
Joshua
|
| 406 |
+
Juan Luis Cano Rodríguez
|
| 407 |
+
Juanjo Bazán
|
| 408 |
+
Judah Rand
|
| 409 |
+
Julian Berman
|
| 410 |
+
Julian Gethmann
|
| 411 |
+
Julien Demoor
|
| 412 |
+
Jussi Kukkonen
|
| 413 |
+
jwg4
|
| 414 |
+
Jyrki Pulliainen
|
| 415 |
+
Kai Chen
|
| 416 |
+
Kai Mueller
|
| 417 |
+
Kamal Bin Mustafa
|
| 418 |
+
kasium
|
| 419 |
+
kaustav haldar
|
| 420 |
+
keanemind
|
| 421 |
+
Keith Maxwell
|
| 422 |
+
Kelsey Hightower
|
| 423 |
+
Kenneth Belitzky
|
| 424 |
+
Kenneth Reitz
|
| 425 |
+
Kevin Burke
|
| 426 |
+
Kevin Carter
|
| 427 |
+
Kevin Frommelt
|
| 428 |
+
Kevin R Patterson
|
| 429 |
+
Kexuan Sun
|
| 430 |
+
Kit Randel
|
| 431 |
+
Klaas van Schelven
|
| 432 |
+
KOLANICH
|
| 433 |
+
konstin
|
| 434 |
+
kpinc
|
| 435 |
+
Krishna Oza
|
| 436 |
+
Kumar McMillan
|
| 437 |
+
Kuntal Majumder
|
| 438 |
+
Kurt McKee
|
| 439 |
+
Kyle Persohn
|
| 440 |
+
lakshmanaram
|
| 441 |
+
Laszlo Kiss-Kollar
|
| 442 |
+
Laurent Bristiel
|
| 443 |
+
Laurent LAPORTE
|
| 444 |
+
Laurie O
|
| 445 |
+
Laurie Opperman
|
| 446 |
+
layday
|
| 447 |
+
Leon Sasson
|
| 448 |
+
Lev Givon
|
| 449 |
+
Lincoln de Sousa
|
| 450 |
+
Lipis
|
| 451 |
+
lorddavidiii
|
| 452 |
+
Loren Carvalho
|
| 453 |
+
Lucas Cimon
|
| 454 |
+
Ludovic Gasc
|
| 455 |
+
Luis Medel
|
| 456 |
+
Lukas Geiger
|
| 457 |
+
Lukas Juhrich
|
| 458 |
+
Luke Macken
|
| 459 |
+
Luo Jiebin
|
| 460 |
+
luojiebin
|
| 461 |
+
luz.paz
|
| 462 |
+
László Kiss Kollár
|
| 463 |
+
M00nL1ght
|
| 464 |
+
Marc Abramowitz
|
| 465 |
+
Marc Tamlyn
|
| 466 |
+
Marcus Smith
|
| 467 |
+
Mariatta
|
| 468 |
+
Mark Kohler
|
| 469 |
+
Mark McLoughlin
|
| 470 |
+
Mark Williams
|
| 471 |
+
Markus Hametner
|
| 472 |
+
Martey Dodoo
|
| 473 |
+
Martin Fischer
|
| 474 |
+
Martin Häcker
|
| 475 |
+
Martin Pavlasek
|
| 476 |
+
Masaki
|
| 477 |
+
Masklinn
|
| 478 |
+
Matej Stuchlik
|
| 479 |
+
Mathew Jennings
|
| 480 |
+
Mathieu Bridon
|
| 481 |
+
Mathieu Kniewallner
|
| 482 |
+
Matt Bacchi
|
| 483 |
+
Matt Good
|
| 484 |
+
Matt Maker
|
| 485 |
+
Matt Robenolt
|
| 486 |
+
Matt Wozniski
|
| 487 |
+
matthew
|
| 488 |
+
Matthew Einhorn
|
| 489 |
+
Matthew Feickert
|
| 490 |
+
Matthew Gilliard
|
| 491 |
+
Matthew Hughes
|
| 492 |
+
Matthew Iversen
|
| 493 |
+
Matthew Treinish
|
| 494 |
+
Matthew Trumbell
|
| 495 |
+
Matthew Willson
|
| 496 |
+
Matthias Bussonnier
|
| 497 |
+
mattip
|
| 498 |
+
Maurits van Rees
|
| 499 |
+
Max W Chase
|
| 500 |
+
Maxim Kurnikov
|
| 501 |
+
Maxime Rouyrre
|
| 502 |
+
mayeut
|
| 503 |
+
mbaluna
|
| 504 |
+
mdebi
|
| 505 |
+
memoselyk
|
| 506 |
+
meowmeowcat
|
| 507 |
+
Michael
|
| 508 |
+
Michael Aquilina
|
| 509 |
+
Michael E. Karpeles
|
| 510 |
+
Michael Klich
|
| 511 |
+
Michael Mintz
|
| 512 |
+
Michael Williamson
|
| 513 |
+
michaelpacer
|
| 514 |
+
Michał Górny
|
| 515 |
+
Mickaël Schoentgen
|
| 516 |
+
Miguel Araujo Perez
|
| 517 |
+
Mihir Singh
|
| 518 |
+
Mike
|
| 519 |
+
Mike Hendricks
|
| 520 |
+
Min RK
|
| 521 |
+
MinRK
|
| 522 |
+
Miro Hrončok
|
| 523 |
+
Monica Baluna
|
| 524 |
+
montefra
|
| 525 |
+
Monty Taylor
|
| 526 |
+
morotti
|
| 527 |
+
mrKazzila
|
| 528 |
+
Muha Ajjan
|
| 529 |
+
Nadav Wexler
|
| 530 |
+
Nahuel Ambrosini
|
| 531 |
+
Nate Coraor
|
| 532 |
+
Nate Prewitt
|
| 533 |
+
Nathan Houghton
|
| 534 |
+
Nathaniel J. Smith
|
| 535 |
+
Nehal J Wani
|
| 536 |
+
Neil Botelho
|
| 537 |
+
Nguyễn Gia Phong
|
| 538 |
+
Nicholas Serra
|
| 539 |
+
Nick Coghlan
|
| 540 |
+
Nick Stenning
|
| 541 |
+
Nick Timkovich
|
| 542 |
+
Nicolas Bock
|
| 543 |
+
Nicole Harris
|
| 544 |
+
Nikhil Benesch
|
| 545 |
+
Nikhil Ladha
|
| 546 |
+
Nikita Chepanov
|
| 547 |
+
Nikolay Korolev
|
| 548 |
+
Nipunn Koorapati
|
| 549 |
+
Nitesh Sharma
|
| 550 |
+
Niyas Sait
|
| 551 |
+
Noah
|
| 552 |
+
Noah Gorny
|
| 553 |
+
Nowell Strite
|
| 554 |
+
NtaleGrey
|
| 555 |
+
nvdv
|
| 556 |
+
OBITORASU
|
| 557 |
+
Ofek Lev
|
| 558 |
+
ofrinevo
|
| 559 |
+
Oliver Freund
|
| 560 |
+
Oliver Jeeves
|
| 561 |
+
Oliver Mannion
|
| 562 |
+
Oliver Tonnhofer
|
| 563 |
+
Olivier Girardot
|
| 564 |
+
Olivier Grisel
|
| 565 |
+
Ollie Rutherfurd
|
| 566 |
+
OMOTO Kenji
|
| 567 |
+
Omry Yadan
|
| 568 |
+
onlinejudge95
|
| 569 |
+
Oren Held
|
| 570 |
+
Oscar Benjamin
|
| 571 |
+
Oz N Tiram
|
| 572 |
+
Pachwenko
|
| 573 |
+
Patrick Dubroy
|
| 574 |
+
Patrick Jenkins
|
| 575 |
+
Patrick Lawson
|
| 576 |
+
patricktokeeffe
|
| 577 |
+
Patrik Kopkan
|
| 578 |
+
Paul Ganssle
|
| 579 |
+
Paul Kehrer
|
| 580 |
+
Paul Moore
|
| 581 |
+
Paul Nasrat
|
| 582 |
+
Paul Oswald
|
| 583 |
+
Paul van der Linden
|
| 584 |
+
Paulus Schoutsen
|
| 585 |
+
Pavel Safronov
|
| 586 |
+
Pavithra Eswaramoorthy
|
| 587 |
+
Pawel Jasinski
|
| 588 |
+
Paweł Szramowski
|
| 589 |
+
Pekka Klärck
|
| 590 |
+
Peter Gessler
|
| 591 |
+
Peter Lisák
|
| 592 |
+
Peter Shen
|
| 593 |
+
Peter Waller
|
| 594 |
+
Petr Viktorin
|
| 595 |
+
petr-tik
|
| 596 |
+
Phaneendra Chiruvella
|
| 597 |
+
Phil Elson
|
| 598 |
+
Phil Freo
|
| 599 |
+
Phil Pennock
|
| 600 |
+
Phil Whelan
|
| 601 |
+
Philip Jägenstedt
|
| 602 |
+
Philip Molloy
|
| 603 |
+
Philippe Ombredanne
|
| 604 |
+
Pi Delport
|
| 605 |
+
Pierre-Yves Rofes
|
| 606 |
+
Pieter Degroote
|
| 607 |
+
pip
|
| 608 |
+
Prabakaran Kumaresshan
|
| 609 |
+
Prabhjyotsing Surjit Singh Sodhi
|
| 610 |
+
Prabhu Marappan
|
| 611 |
+
Pradyun Gedam
|
| 612 |
+
Prashant Sharma
|
| 613 |
+
Pratik Mallya
|
| 614 |
+
pre-commit-ci[bot]
|
| 615 |
+
Preet Thakkar
|
| 616 |
+
Preston Holmes
|
| 617 |
+
Przemek Wrzos
|
| 618 |
+
Pulkit Goyal
|
| 619 |
+
q0w
|
| 620 |
+
Qiangning Hong
|
| 621 |
+
Qiming Xu
|
| 622 |
+
Quentin Lee
|
| 623 |
+
Quentin Pradet
|
| 624 |
+
R. David Murray
|
| 625 |
+
Rafael Caricio
|
| 626 |
+
Ralf Schmitt
|
| 627 |
+
Ran Benita
|
| 628 |
+
Razzi Abuissa
|
| 629 |
+
rdb
|
| 630 |
+
Reece Dunham
|
| 631 |
+
Remi Rampin
|
| 632 |
+
Rene Dudfield
|
| 633 |
+
Riccardo Magliocchetti
|
| 634 |
+
Riccardo Schirone
|
| 635 |
+
Richard Jones
|
| 636 |
+
Richard Si
|
| 637 |
+
Ricky Ng-Adam
|
| 638 |
+
Rishi
|
| 639 |
+
rmorotti
|
| 640 |
+
RobberPhex
|
| 641 |
+
Robert Collins
|
| 642 |
+
Robert McGibbon
|
| 643 |
+
Robert Pollak
|
| 644 |
+
Robert T. McGibbon
|
| 645 |
+
robin elisha robinson
|
| 646 |
+
Roey Berman
|
| 647 |
+
Rohan Jain
|
| 648 |
+
Roman Bogorodskiy
|
| 649 |
+
Roman Donchenko
|
| 650 |
+
Romuald Brunet
|
| 651 |
+
ronaudinho
|
| 652 |
+
Ronny Pfannschmidt
|
| 653 |
+
Rory McCann
|
| 654 |
+
Ross Brattain
|
| 655 |
+
Roy Wellington Ⅳ
|
| 656 |
+
Ruairidh MacLeod
|
| 657 |
+
Russell Keith-Magee
|
| 658 |
+
Ryan Shepherd
|
| 659 |
+
Ryan Wooden
|
| 660 |
+
ryneeverett
|
| 661 |
+
S. Guliaev
|
| 662 |
+
Sachi King
|
| 663 |
+
Salvatore Rinchiera
|
| 664 |
+
sandeepkiran-js
|
| 665 |
+
Sander Van Balen
|
| 666 |
+
Savio Jomton
|
| 667 |
+
schlamar
|
| 668 |
+
Scott Kitterman
|
| 669 |
+
Sean
|
| 670 |
+
seanj
|
| 671 |
+
Sebastian Jordan
|
| 672 |
+
Sebastian Schaetz
|
| 673 |
+
Segev Finer
|
| 674 |
+
SeongSoo Cho
|
| 675 |
+
Sergey Vasilyev
|
| 676 |
+
Seth Michael Larson
|
| 677 |
+
Seth Woodworth
|
| 678 |
+
Shahar Epstein
|
| 679 |
+
Shantanu
|
| 680 |
+
shenxianpeng
|
| 681 |
+
shireenrao
|
| 682 |
+
Shivansh-007
|
| 683 |
+
Shixian Sheng
|
| 684 |
+
Shlomi Fish
|
| 685 |
+
Shovan Maity
|
| 686 |
+
Simeon Visser
|
| 687 |
+
Simon Cross
|
| 688 |
+
Simon Pichugin
|
| 689 |
+
sinoroc
|
| 690 |
+
sinscary
|
| 691 |
+
snook92
|
| 692 |
+
socketubs
|
| 693 |
+
Sorin Sbarnea
|
| 694 |
+
Srinivas Nyayapati
|
| 695 |
+
Srishti Hegde
|
| 696 |
+
Stavros Korokithakis
|
| 697 |
+
Stefan Scherfke
|
| 698 |
+
Stefano Rivera
|
| 699 |
+
Stephan Erb
|
| 700 |
+
Stephen Rosen
|
| 701 |
+
stepshal
|
| 702 |
+
Steve (Gadget) Barnes
|
| 703 |
+
Steve Barnes
|
| 704 |
+
Steve Dower
|
| 705 |
+
Steve Kowalik
|
| 706 |
+
Steven Myint
|
| 707 |
+
Steven Silvester
|
| 708 |
+
stonebig
|
| 709 |
+
studioj
|
| 710 |
+
Stéphane Bidoul
|
| 711 |
+
Stéphane Bidoul (ACSONE)
|
| 712 |
+
Stéphane Klein
|
| 713 |
+
Sumana Harihareswara
|
| 714 |
+
Surbhi Sharma
|
| 715 |
+
Sviatoslav Sydorenko
|
| 716 |
+
Sviatoslav Sydorenko (Святослав Сидоренко)
|
| 717 |
+
Swat009
|
| 718 |
+
Sylvain
|
| 719 |
+
Takayuki SHIMIZUKAWA
|
| 720 |
+
Taneli Hukkinen
|
| 721 |
+
tbeswick
|
| 722 |
+
Thiago
|
| 723 |
+
Thijs Triemstra
|
| 724 |
+
Thomas Fenzl
|
| 725 |
+
Thomas Grainger
|
| 726 |
+
Thomas Guettler
|
| 727 |
+
Thomas Johansson
|
| 728 |
+
Thomas Kluyver
|
| 729 |
+
Thomas Smith
|
| 730 |
+
Thomas VINCENT
|
| 731 |
+
Tim D. Smith
|
| 732 |
+
Tim Gates
|
| 733 |
+
Tim Harder
|
| 734 |
+
Tim Heap
|
| 735 |
+
tim smith
|
| 736 |
+
tinruufu
|
| 737 |
+
Tobias Hermann
|
| 738 |
+
Tom Forbes
|
| 739 |
+
Tom Freudenheim
|
| 740 |
+
Tom V
|
| 741 |
+
Tomas Hrnciar
|
| 742 |
+
Tomas Orsava
|
| 743 |
+
Tomer Chachamu
|
| 744 |
+
Tommi Enenkel | AnB
|
| 745 |
+
Tomáš Hrnčiar
|
| 746 |
+
Tony Beswick
|
| 747 |
+
Tony Narlock
|
| 748 |
+
Tony Zhaocheng Tan
|
| 749 |
+
TonyBeswick
|
| 750 |
+
toonarmycaptain
|
| 751 |
+
Toshio Kuratomi
|
| 752 |
+
toxinu
|
| 753 |
+
Travis Swicegood
|
| 754 |
+
Tushar Sadhwani
|
| 755 |
+
Tzu-ping Chung
|
| 756 |
+
Valentin Haenel
|
| 757 |
+
Victor Stinner
|
| 758 |
+
victorvpaulo
|
| 759 |
+
Vikram - Google
|
| 760 |
+
Viktor Szépe
|
| 761 |
+
Ville Skyttä
|
| 762 |
+
Vinay Sajip
|
| 763 |
+
Vincent Philippon
|
| 764 |
+
Vinicyus Macedo
|
| 765 |
+
Vipul Kumar
|
| 766 |
+
Vitaly Babiy
|
| 767 |
+
Vladimir Fokow
|
| 768 |
+
Vladimir Rutsky
|
| 769 |
+
W. Trevor King
|
| 770 |
+
Wil Tan
|
| 771 |
+
Wilfred Hughes
|
| 772 |
+
William Edwards
|
| 773 |
+
William ML Leslie
|
| 774 |
+
William T Olson
|
| 775 |
+
William Woodruff
|
| 776 |
+
Wilson Mo
|
| 777 |
+
wim glenn
|
| 778 |
+
Winson Luk
|
| 779 |
+
Wolfgang Maier
|
| 780 |
+
Wu Zhenyu
|
| 781 |
+
XAMES3
|
| 782 |
+
Xavier Fernandez
|
| 783 |
+
Xianpeng Shen
|
| 784 |
+
xoviat
|
| 785 |
+
xtreak
|
| 786 |
+
YAMAMOTO Takashi
|
| 787 |
+
Yen Chi Hsuan
|
| 788 |
+
Yeray Diaz Diaz
|
| 789 |
+
Yoval P
|
| 790 |
+
Yu Jian
|
| 791 |
+
Yuan Jing Vincent Yan
|
| 792 |
+
Yusuke Hayashi
|
| 793 |
+
Zearin
|
| 794 |
+
Zhiping Deng
|
| 795 |
+
ziebam
|
| 796 |
+
Zvezdan Petkovic
|
| 797 |
+
Łukasz Langa
|
| 798 |
+
Роман Донченко
|
| 799 |
+
Семён Марьясин
|
venv/lib/python3.12/site-packages/pip-24.3.1.dist-info/INSTALLER
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
pip
|
venv/lib/python3.12/site-packages/pip-24.3.1.dist-info/LICENSE.txt
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Copyright (c) 2008-present The pip developers (see AUTHORS.txt file)
|
| 2 |
+
|
| 3 |
+
Permission is hereby granted, free of charge, to any person obtaining
|
| 4 |
+
a copy of this software and associated documentation files (the
|
| 5 |
+
"Software"), to deal in the Software without restriction, including
|
| 6 |
+
without limitation the rights to use, copy, modify, merge, publish,
|
| 7 |
+
distribute, sublicense, and/or sell copies of the Software, and to
|
| 8 |
+
permit persons to whom the Software is furnished to do so, subject to
|
| 9 |
+
the following conditions:
|
| 10 |
+
|
| 11 |
+
The above copyright notice and this permission notice shall be
|
| 12 |
+
included in all copies or substantial portions of the Software.
|
| 13 |
+
|
| 14 |
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
| 15 |
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
| 16 |
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
| 17 |
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
| 18 |
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
| 19 |
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
| 20 |
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
venv/lib/python3.12/site-packages/pip-24.3.1.dist-info/METADATA
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Metadata-Version: 2.1
|
| 2 |
+
Name: pip
|
| 3 |
+
Version: 24.3.1
|
| 4 |
+
Summary: The PyPA recommended tool for installing Python packages.
|
| 5 |
+
Author-email: The pip developers <distutils-sig@python.org>
|
| 6 |
+
License: MIT
|
| 7 |
+
Project-URL: Homepage, https://pip.pypa.io/
|
| 8 |
+
Project-URL: Documentation, https://pip.pypa.io
|
| 9 |
+
Project-URL: Source, https://github.com/pypa/pip
|
| 10 |
+
Project-URL: Changelog, https://pip.pypa.io/en/stable/news/
|
| 11 |
+
Classifier: Development Status :: 5 - Production/Stable
|
| 12 |
+
Classifier: Intended Audience :: Developers
|
| 13 |
+
Classifier: License :: OSI Approved :: MIT License
|
| 14 |
+
Classifier: Topic :: Software Development :: Build Tools
|
| 15 |
+
Classifier: Programming Language :: Python
|
| 16 |
+
Classifier: Programming Language :: Python :: 3
|
| 17 |
+
Classifier: Programming Language :: Python :: 3 :: Only
|
| 18 |
+
Classifier: Programming Language :: Python :: 3.8
|
| 19 |
+
Classifier: Programming Language :: Python :: 3.9
|
| 20 |
+
Classifier: Programming Language :: Python :: 3.10
|
| 21 |
+
Classifier: Programming Language :: Python :: 3.11
|
| 22 |
+
Classifier: Programming Language :: Python :: 3.12
|
| 23 |
+
Classifier: Programming Language :: Python :: 3.13
|
| 24 |
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
| 25 |
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
| 26 |
+
Requires-Python: >=3.8
|
| 27 |
+
Description-Content-Type: text/x-rst
|
| 28 |
+
License-File: LICENSE.txt
|
| 29 |
+
License-File: AUTHORS.txt
|
| 30 |
+
|
| 31 |
+
pip - The Python Package Installer
|
| 32 |
+
==================================
|
| 33 |
+
|
| 34 |
+
.. |pypi-version| image:: https://img.shields.io/pypi/v/pip.svg
|
| 35 |
+
:target: https://pypi.org/project/pip/
|
| 36 |
+
:alt: PyPI
|
| 37 |
+
|
| 38 |
+
.. |python-versions| image:: https://img.shields.io/pypi/pyversions/pip
|
| 39 |
+
:target: https://pypi.org/project/pip
|
| 40 |
+
:alt: PyPI - Python Version
|
| 41 |
+
|
| 42 |
+
.. |docs-badge| image:: https://readthedocs.org/projects/pip/badge/?version=latest
|
| 43 |
+
:target: https://pip.pypa.io/en/latest
|
| 44 |
+
:alt: Documentation
|
| 45 |
+
|
| 46 |
+
|pypi-version| |python-versions| |docs-badge|
|
| 47 |
+
|
| 48 |
+
pip is the `package installer`_ for Python. You can use pip to install packages from the `Python Package Index`_ and other indexes.
|
| 49 |
+
|
| 50 |
+
Please take a look at our documentation for how to install and use pip:
|
| 51 |
+
|
| 52 |
+
* `Installation`_
|
| 53 |
+
* `Usage`_
|
| 54 |
+
|
| 55 |
+
We release updates regularly, with a new version every 3 months. Find more details in our documentation:
|
| 56 |
+
|
| 57 |
+
* `Release notes`_
|
| 58 |
+
* `Release process`_
|
| 59 |
+
|
| 60 |
+
If you find bugs, need help, or want to talk to the developers, please use our mailing lists or chat rooms:
|
| 61 |
+
|
| 62 |
+
* `Issue tracking`_
|
| 63 |
+
* `Discourse channel`_
|
| 64 |
+
* `User IRC`_
|
| 65 |
+
|
| 66 |
+
If you want to get involved head over to GitHub to get the source code, look at our development documentation and feel free to jump on the developer mailing lists and chat rooms:
|
| 67 |
+
|
| 68 |
+
* `GitHub page`_
|
| 69 |
+
* `Development documentation`_
|
| 70 |
+
* `Development IRC`_
|
| 71 |
+
|
| 72 |
+
Code of Conduct
|
| 73 |
+
---------------
|
| 74 |
+
|
| 75 |
+
Everyone interacting in the pip project's codebases, issue trackers, chat
|
| 76 |
+
rooms, and mailing lists is expected to follow the `PSF Code of Conduct`_.
|
| 77 |
+
|
| 78 |
+
.. _package installer: https://packaging.python.org/guides/tool-recommendations/
|
| 79 |
+
.. _Python Package Index: https://pypi.org
|
| 80 |
+
.. _Installation: https://pip.pypa.io/en/stable/installation/
|
| 81 |
+
.. _Usage: https://pip.pypa.io/en/stable/
|
| 82 |
+
.. _Release notes: https://pip.pypa.io/en/stable/news.html
|
| 83 |
+
.. _Release process: https://pip.pypa.io/en/latest/development/release-process/
|
| 84 |
+
.. _GitHub page: https://github.com/pypa/pip
|
| 85 |
+
.. _Development documentation: https://pip.pypa.io/en/latest/development
|
| 86 |
+
.. _Issue tracking: https://github.com/pypa/pip/issues
|
| 87 |
+
.. _Discourse channel: https://discuss.python.org/c/packaging
|
| 88 |
+
.. _User IRC: https://kiwiirc.com/nextclient/#ircs://irc.libera.chat:+6697/pypa
|
| 89 |
+
.. _Development IRC: https://kiwiirc.com/nextclient/#ircs://irc.libera.chat:+6697/pypa-dev
|
| 90 |
+
.. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md
|
venv/lib/python3.12/site-packages/pip-24.3.1.dist-info/RECORD
ADDED
|
@@ -0,0 +1,853 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
../../../bin/pip,sha256=-GAEslaMUmzRvnu-5P-uZ9Y9RYbZJJs3TaMHZj_bRUw,259
|
| 2 |
+
../../../bin/pip3,sha256=-GAEslaMUmzRvnu-5P-uZ9Y9RYbZJJs3TaMHZj_bRUw,259
|
| 3 |
+
../../../bin/pip3.12,sha256=-GAEslaMUmzRvnu-5P-uZ9Y9RYbZJJs3TaMHZj_bRUw,259
|
| 4 |
+
pip-24.3.1.dist-info/AUTHORS.txt,sha256=Cbb630k8EL9FkBzX9Vpi6hpYWrLSlh08eXodL5u0eLI,10925
|
| 5 |
+
pip-24.3.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
| 6 |
+
pip-24.3.1.dist-info/LICENSE.txt,sha256=Y0MApmnUmurmWxLGxIySTFGkzfPR_whtw0VtyLyqIQQ,1093
|
| 7 |
+
pip-24.3.1.dist-info/METADATA,sha256=V8iCNK1GYbC82PWsLMsASDh9AO4veocRlM4Pn9q2KFI,3677
|
| 8 |
+
pip-24.3.1.dist-info/RECORD,,
|
| 9 |
+
pip-24.3.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 10 |
+
pip-24.3.1.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
|
| 11 |
+
pip-24.3.1.dist-info/entry_points.txt,sha256=eeIjuzfnfR2PrhbjnbzFU6MnSS70kZLxwaHHq6M-bD0,87
|
| 12 |
+
pip-24.3.1.dist-info/top_level.txt,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
|
| 13 |
+
pip/__init__.py,sha256=faXY_neeYrA_88plEhkyhwAaYeds7wu5U1iGwP24J0s,357
|
| 14 |
+
pip/__main__.py,sha256=WzbhHXTbSE6gBY19mNN9m4s5o_365LOvTYSgqgbdBhE,854
|
| 15 |
+
pip/__pip-runner__.py,sha256=cPPWuJ6NK_k-GzfvlejLFgwzmYUROmpAR6QC3Q-vkXQ,1450
|
| 16 |
+
pip/__pycache__/__init__.cpython-312.pyc,,
|
| 17 |
+
pip/__pycache__/__main__.cpython-312.pyc,,
|
| 18 |
+
pip/__pycache__/__pip-runner__.cpython-312.pyc,,
|
| 19 |
+
pip/_internal/__init__.py,sha256=MfcoOluDZ8QMCFYal04IqOJ9q6m2V7a0aOsnI-WOxUo,513
|
| 20 |
+
pip/_internal/__pycache__/__init__.cpython-312.pyc,,
|
| 21 |
+
pip/_internal/__pycache__/build_env.cpython-312.pyc,,
|
| 22 |
+
pip/_internal/__pycache__/cache.cpython-312.pyc,,
|
| 23 |
+
pip/_internal/__pycache__/configuration.cpython-312.pyc,,
|
| 24 |
+
pip/_internal/__pycache__/exceptions.cpython-312.pyc,,
|
| 25 |
+
pip/_internal/__pycache__/main.cpython-312.pyc,,
|
| 26 |
+
pip/_internal/__pycache__/pyproject.cpython-312.pyc,,
|
| 27 |
+
pip/_internal/__pycache__/self_outdated_check.cpython-312.pyc,,
|
| 28 |
+
pip/_internal/__pycache__/wheel_builder.cpython-312.pyc,,
|
| 29 |
+
pip/_internal/build_env.py,sha256=wsTPOWyPTKvUREUcO585OU01kbQufpdigY8fVHv3WIw,10584
|
| 30 |
+
pip/_internal/cache.py,sha256=Jb698p5PNigRtpW5o26wQNkkUv4MnQ94mc471wL63A0,10369
|
| 31 |
+
pip/_internal/cli/__init__.py,sha256=FkHBgpxxb-_gd6r1FjnNhfMOzAUYyXoXKJ6abijfcFU,132
|
| 32 |
+
pip/_internal/cli/__pycache__/__init__.cpython-312.pyc,,
|
| 33 |
+
pip/_internal/cli/__pycache__/autocompletion.cpython-312.pyc,,
|
| 34 |
+
pip/_internal/cli/__pycache__/base_command.cpython-312.pyc,,
|
| 35 |
+
pip/_internal/cli/__pycache__/cmdoptions.cpython-312.pyc,,
|
| 36 |
+
pip/_internal/cli/__pycache__/command_context.cpython-312.pyc,,
|
| 37 |
+
pip/_internal/cli/__pycache__/index_command.cpython-312.pyc,,
|
| 38 |
+
pip/_internal/cli/__pycache__/main.cpython-312.pyc,,
|
| 39 |
+
pip/_internal/cli/__pycache__/main_parser.cpython-312.pyc,,
|
| 40 |
+
pip/_internal/cli/__pycache__/parser.cpython-312.pyc,,
|
| 41 |
+
pip/_internal/cli/__pycache__/progress_bars.cpython-312.pyc,,
|
| 42 |
+
pip/_internal/cli/__pycache__/req_command.cpython-312.pyc,,
|
| 43 |
+
pip/_internal/cli/__pycache__/spinners.cpython-312.pyc,,
|
| 44 |
+
pip/_internal/cli/__pycache__/status_codes.cpython-312.pyc,,
|
| 45 |
+
pip/_internal/cli/autocompletion.py,sha256=Lli3Mr6aDNu7ZkJJFFvwD2-hFxNI6Avz8OwMyS5TVrs,6865
|
| 46 |
+
pip/_internal/cli/base_command.py,sha256=F8nUcSM-Y-MQljJUe724-yxmc5viFXHyM_zH70NmIh4,8289
|
| 47 |
+
pip/_internal/cli/cmdoptions.py,sha256=mDqBr0d0hoztbRJs-PWtcKpqNAc7khU6ZpoesZKocT8,30110
|
| 48 |
+
pip/_internal/cli/command_context.py,sha256=RHgIPwtObh5KhMrd3YZTkl8zbVG-6Okml7YbFX4Ehg0,774
|
| 49 |
+
pip/_internal/cli/index_command.py,sha256=-0oPTruZGkLSMrWDleZ6UtcKP3G-SImRRuhH0RfVE3o,5631
|
| 50 |
+
pip/_internal/cli/main.py,sha256=BDZef-bWe9g9Jpr4OVs4dDf-845HJsKw835T7AqEnAc,2817
|
| 51 |
+
pip/_internal/cli/main_parser.py,sha256=laDpsuBDl6kyfywp9eMMA9s84jfH2TJJn-vmL0GG90w,4338
|
| 52 |
+
pip/_internal/cli/parser.py,sha256=VCMtduzECUV87KaHNu-xJ-wLNL82yT3x16V4XBxOAqI,10825
|
| 53 |
+
pip/_internal/cli/progress_bars.py,sha256=VgydyqjZvfhqpuNcFDn00QNuA9GxRe9CKrRG8jhPuKU,2723
|
| 54 |
+
pip/_internal/cli/req_command.py,sha256=DqeFhmUMs6o6Ev8qawAcOoYNdAZsfyKS0MZI5jsJYwQ,12250
|
| 55 |
+
pip/_internal/cli/spinners.py,sha256=hIJ83GerdFgFCdobIA23Jggetegl_uC4Sp586nzFbPE,5118
|
| 56 |
+
pip/_internal/cli/status_codes.py,sha256=sEFHUaUJbqv8iArL3HAtcztWZmGOFX01hTesSytDEh0,116
|
| 57 |
+
pip/_internal/commands/__init__.py,sha256=5oRO9O3dM2vGuh0bFw4HOVletryrz5HHMmmPWwJrH9U,3882
|
| 58 |
+
pip/_internal/commands/__pycache__/__init__.cpython-312.pyc,,
|
| 59 |
+
pip/_internal/commands/__pycache__/cache.cpython-312.pyc,,
|
| 60 |
+
pip/_internal/commands/__pycache__/check.cpython-312.pyc,,
|
| 61 |
+
pip/_internal/commands/__pycache__/completion.cpython-312.pyc,,
|
| 62 |
+
pip/_internal/commands/__pycache__/configuration.cpython-312.pyc,,
|
| 63 |
+
pip/_internal/commands/__pycache__/debug.cpython-312.pyc,,
|
| 64 |
+
pip/_internal/commands/__pycache__/download.cpython-312.pyc,,
|
| 65 |
+
pip/_internal/commands/__pycache__/freeze.cpython-312.pyc,,
|
| 66 |
+
pip/_internal/commands/__pycache__/hash.cpython-312.pyc,,
|
| 67 |
+
pip/_internal/commands/__pycache__/help.cpython-312.pyc,,
|
| 68 |
+
pip/_internal/commands/__pycache__/index.cpython-312.pyc,,
|
| 69 |
+
pip/_internal/commands/__pycache__/inspect.cpython-312.pyc,,
|
| 70 |
+
pip/_internal/commands/__pycache__/install.cpython-312.pyc,,
|
| 71 |
+
pip/_internal/commands/__pycache__/list.cpython-312.pyc,,
|
| 72 |
+
pip/_internal/commands/__pycache__/search.cpython-312.pyc,,
|
| 73 |
+
pip/_internal/commands/__pycache__/show.cpython-312.pyc,,
|
| 74 |
+
pip/_internal/commands/__pycache__/uninstall.cpython-312.pyc,,
|
| 75 |
+
pip/_internal/commands/__pycache__/wheel.cpython-312.pyc,,
|
| 76 |
+
pip/_internal/commands/cache.py,sha256=xg76_ZFEBC6zoQ3gXLRfMZJft4z2a0RwH4GEFZC6nnU,7944
|
| 77 |
+
pip/_internal/commands/check.py,sha256=Hr_4eiMd9cgVDgEvjtIdw915NmL7ROIWW8enkr8slPQ,2268
|
| 78 |
+
pip/_internal/commands/completion.py,sha256=HT4lD0bgsflHq2IDgYfiEdp7IGGtE7s6MgI3xn0VQEw,4287
|
| 79 |
+
pip/_internal/commands/configuration.py,sha256=n98enwp6y0b5G6fiRQjaZo43FlJKYve_daMhN-4BRNc,9766
|
| 80 |
+
pip/_internal/commands/debug.py,sha256=DNDRgE9YsKrbYzU0s3VKi8rHtKF4X13CJ_br_8PUXO0,6797
|
| 81 |
+
pip/_internal/commands/download.py,sha256=0qB0nys6ZEPsog451lDsjL5Bx7Z97t-B80oFZKhpzKM,5273
|
| 82 |
+
pip/_internal/commands/freeze.py,sha256=2Vt72BYTSm9rzue6d8dNzt8idxWK4Db6Hd-anq7GQ80,3203
|
| 83 |
+
pip/_internal/commands/hash.py,sha256=EVVOuvGtoPEdFi8SNnmdqlCQrhCxV-kJsdwtdcCnXGQ,1703
|
| 84 |
+
pip/_internal/commands/help.py,sha256=gcc6QDkcgHMOuAn5UxaZwAStsRBrnGSn_yxjS57JIoM,1132
|
| 85 |
+
pip/_internal/commands/index.py,sha256=RAXxmJwFhVb5S1BYzb5ifX3sn9Na8v2CCVYwSMP8pao,4731
|
| 86 |
+
pip/_internal/commands/inspect.py,sha256=PGrY9TRTRCM3y5Ml8Bdk8DEOXquWRfscr4DRo1LOTPc,3189
|
| 87 |
+
pip/_internal/commands/install.py,sha256=iqesiLIZc6Op9uihMQFYRhAA2DQRZUxbM4z1BwXoFls,29428
|
| 88 |
+
pip/_internal/commands/list.py,sha256=oiIzSjLP6__d7dIS3q0Xb5ywsaOThBWRqMyjjKzkPdM,12769
|
| 89 |
+
pip/_internal/commands/search.py,sha256=fWkUQVx_gm8ebbFAlCgqtxKXT9rNahpJ-BI__3HNZpg,5626
|
| 90 |
+
pip/_internal/commands/show.py,sha256=IG9L5uo8w6UA4tI_IlmaxLCoNKPa5JNJCljj3NWs0OE,7507
|
| 91 |
+
pip/_internal/commands/uninstall.py,sha256=7pOR7enK76gimyxQbzxcG1OsyLXL3DvX939xmM8Fvtg,3892
|
| 92 |
+
pip/_internal/commands/wheel.py,sha256=eJRhr_qoNNxWAkkdJCNiQM7CXd4E1_YyQhsqJnBPGGg,6414
|
| 93 |
+
pip/_internal/configuration.py,sha256=XkAiBS0hpzsM-LF0Qu5hvPWO_Bs67-oQKRYFBuMbESs,14006
|
| 94 |
+
pip/_internal/distributions/__init__.py,sha256=Hq6kt6gXBgjNit5hTTWLAzeCNOKoB-N0pGYSqehrli8,858
|
| 95 |
+
pip/_internal/distributions/__pycache__/__init__.cpython-312.pyc,,
|
| 96 |
+
pip/_internal/distributions/__pycache__/base.cpython-312.pyc,,
|
| 97 |
+
pip/_internal/distributions/__pycache__/installed.cpython-312.pyc,,
|
| 98 |
+
pip/_internal/distributions/__pycache__/sdist.cpython-312.pyc,,
|
| 99 |
+
pip/_internal/distributions/__pycache__/wheel.cpython-312.pyc,,
|
| 100 |
+
pip/_internal/distributions/base.py,sha256=QeB9qvKXDIjLdPBDE5fMgpfGqMMCr-govnuoQnGuiF8,1783
|
| 101 |
+
pip/_internal/distributions/installed.py,sha256=QinHFbWAQ8oE0pbD8MFZWkwlnfU1QYTccA1vnhrlYOU,842
|
| 102 |
+
pip/_internal/distributions/sdist.py,sha256=PlcP4a6-R6c98XnOM-b6Lkb3rsvh9iG4ok8shaanrzs,6751
|
| 103 |
+
pip/_internal/distributions/wheel.py,sha256=THBYfnv7VVt8mYhMYUtH13S1E7FDwtDyDfmUcl8ai0E,1317
|
| 104 |
+
pip/_internal/exceptions.py,sha256=2_byISIv3kSnI_9T-Esfxrt0LnTRgcUHyxu0twsHjQY,26481
|
| 105 |
+
pip/_internal/index/__init__.py,sha256=vpt-JeTZefh8a-FC22ZeBSXFVbuBcXSGiILhQZJaNpQ,30
|
| 106 |
+
pip/_internal/index/__pycache__/__init__.cpython-312.pyc,,
|
| 107 |
+
pip/_internal/index/__pycache__/collector.cpython-312.pyc,,
|
| 108 |
+
pip/_internal/index/__pycache__/package_finder.cpython-312.pyc,,
|
| 109 |
+
pip/_internal/index/__pycache__/sources.cpython-312.pyc,,
|
| 110 |
+
pip/_internal/index/collector.py,sha256=RdPO0JLAlmyBWPAWYHPyRoGjz3GNAeTngCNkbGey_mE,16265
|
| 111 |
+
pip/_internal/index/package_finder.py,sha256=yRC4xsyudwKnNoU6IXvNoyqYo5ScT7lB6Wa-z2eh7cs,37666
|
| 112 |
+
pip/_internal/index/sources.py,sha256=lPBLK5Xiy8Q6IQMio26Wl7ocfZOKkgGklIBNyUJ23fI,8632
|
| 113 |
+
pip/_internal/locations/__init__.py,sha256=UaAxeZ_f93FyouuFf4p7SXYF-4WstXuEvd3LbmPCAno,14925
|
| 114 |
+
pip/_internal/locations/__pycache__/__init__.cpython-312.pyc,,
|
| 115 |
+
pip/_internal/locations/__pycache__/_distutils.cpython-312.pyc,,
|
| 116 |
+
pip/_internal/locations/__pycache__/_sysconfig.cpython-312.pyc,,
|
| 117 |
+
pip/_internal/locations/__pycache__/base.cpython-312.pyc,,
|
| 118 |
+
pip/_internal/locations/_distutils.py,sha256=x6nyVLj7X11Y4khIdf-mFlxMl2FWadtVEgeb8upc_WI,6013
|
| 119 |
+
pip/_internal/locations/_sysconfig.py,sha256=IGzds60qsFneRogC-oeBaY7bEh3lPt_v47kMJChQXsU,7724
|
| 120 |
+
pip/_internal/locations/base.py,sha256=RQiPi1d4FVM2Bxk04dQhXZ2PqkeljEL2fZZ9SYqIQ78,2556
|
| 121 |
+
pip/_internal/main.py,sha256=r-UnUe8HLo5XFJz8inTcOOTiu_sxNhgHb6VwlGUllOI,340
|
| 122 |
+
pip/_internal/metadata/__init__.py,sha256=9pU3W3s-6HtjFuYhWcLTYVmSaziklPv7k2x8p7X1GmA,4339
|
| 123 |
+
pip/_internal/metadata/__pycache__/__init__.cpython-312.pyc,,
|
| 124 |
+
pip/_internal/metadata/__pycache__/_json.cpython-312.pyc,,
|
| 125 |
+
pip/_internal/metadata/__pycache__/base.cpython-312.pyc,,
|
| 126 |
+
pip/_internal/metadata/__pycache__/pkg_resources.cpython-312.pyc,,
|
| 127 |
+
pip/_internal/metadata/_json.py,sha256=P0cAJrH_mtmMZvlZ16ZXm_-izA4lpr5wy08laICuiaA,2644
|
| 128 |
+
pip/_internal/metadata/base.py,sha256=ft0K5XNgI4ETqZnRv2-CtvgYiMOMAeGMAzxT-f6VLJA,25298
|
| 129 |
+
pip/_internal/metadata/importlib/__init__.py,sha256=jUUidoxnHcfITHHaAWG1G2i5fdBYklv_uJcjo2x7VYE,135
|
| 130 |
+
pip/_internal/metadata/importlib/__pycache__/__init__.cpython-312.pyc,,
|
| 131 |
+
pip/_internal/metadata/importlib/__pycache__/_compat.cpython-312.pyc,,
|
| 132 |
+
pip/_internal/metadata/importlib/__pycache__/_dists.cpython-312.pyc,,
|
| 133 |
+
pip/_internal/metadata/importlib/__pycache__/_envs.cpython-312.pyc,,
|
| 134 |
+
pip/_internal/metadata/importlib/_compat.py,sha256=c6av8sP8BBjAZuFSJow1iWfygUXNM3xRTCn5nqw6B9M,2796
|
| 135 |
+
pip/_internal/metadata/importlib/_dists.py,sha256=anh0mLI-FYRPUhAdipd0Va3YJJc6HelCKQ0bFhY10a0,8017
|
| 136 |
+
pip/_internal/metadata/importlib/_envs.py,sha256=UUB980XSrDWrMpQ1_G45i0r8Hqlg_tg3IPQ63mEqbNc,7431
|
| 137 |
+
pip/_internal/metadata/pkg_resources.py,sha256=U07ETAINSGeSRBfWUG93E4tZZbaW_f7PGzEqZN0hulc,10542
|
| 138 |
+
pip/_internal/models/__init__.py,sha256=3DHUd_qxpPozfzouoqa9g9ts1Czr5qaHfFxbnxriepM,63
|
| 139 |
+
pip/_internal/models/__pycache__/__init__.cpython-312.pyc,,
|
| 140 |
+
pip/_internal/models/__pycache__/candidate.cpython-312.pyc,,
|
| 141 |
+
pip/_internal/models/__pycache__/direct_url.cpython-312.pyc,,
|
| 142 |
+
pip/_internal/models/__pycache__/format_control.cpython-312.pyc,,
|
| 143 |
+
pip/_internal/models/__pycache__/index.cpython-312.pyc,,
|
| 144 |
+
pip/_internal/models/__pycache__/installation_report.cpython-312.pyc,,
|
| 145 |
+
pip/_internal/models/__pycache__/link.cpython-312.pyc,,
|
| 146 |
+
pip/_internal/models/__pycache__/scheme.cpython-312.pyc,,
|
| 147 |
+
pip/_internal/models/__pycache__/search_scope.cpython-312.pyc,,
|
| 148 |
+
pip/_internal/models/__pycache__/selection_prefs.cpython-312.pyc,,
|
| 149 |
+
pip/_internal/models/__pycache__/target_python.cpython-312.pyc,,
|
| 150 |
+
pip/_internal/models/__pycache__/wheel.cpython-312.pyc,,
|
| 151 |
+
pip/_internal/models/candidate.py,sha256=zzgFRuw_kWPjKpGw7LC0ZUMD2CQ2EberUIYs8izjdCA,753
|
| 152 |
+
pip/_internal/models/direct_url.py,sha256=uBtY2HHd3TO9cKQJWh0ThvE5FRr-MWRYChRU4IG9HZE,6578
|
| 153 |
+
pip/_internal/models/format_control.py,sha256=wtsQqSK9HaUiNxQEuB-C62eVimw6G4_VQFxV9-_KDBE,2486
|
| 154 |
+
pip/_internal/models/index.py,sha256=tYnL8oxGi4aSNWur0mG8DAP7rC6yuha_MwJO8xw0crI,1030
|
| 155 |
+
pip/_internal/models/installation_report.py,sha256=zRVZoaz-2vsrezj_H3hLOhMZCK9c7TbzWgC-jOalD00,2818
|
| 156 |
+
pip/_internal/models/link.py,sha256=jHax9O-9zlSzEwjBCDkx0OXjKXwBDwOuPwn-PsR8dCs,21034
|
| 157 |
+
pip/_internal/models/scheme.py,sha256=PakmHJM3e8OOWSZFtfz1Az7f1meONJnkGuQxFlt3wBE,575
|
| 158 |
+
pip/_internal/models/search_scope.py,sha256=67NEnsYY84784S-MM7ekQuo9KXLH-7MzFntXjapvAo0,4531
|
| 159 |
+
pip/_internal/models/selection_prefs.py,sha256=qaFfDs3ciqoXPg6xx45N1jPLqccLJw4N0s4P0PyHTQ8,2015
|
| 160 |
+
pip/_internal/models/target_python.py,sha256=2XaH2rZ5ZF-K5wcJbEMGEl7SqrTToDDNkrtQ2v_v_-Q,4271
|
| 161 |
+
pip/_internal/models/wheel.py,sha256=G7dND_s4ebPkEL7RJ1qCY0QhUUWIIK6AnjWgRATF5no,4539
|
| 162 |
+
pip/_internal/network/__init__.py,sha256=jf6Tt5nV_7zkARBrKojIXItgejvoegVJVKUbhAa5Ioc,50
|
| 163 |
+
pip/_internal/network/__pycache__/__init__.cpython-312.pyc,,
|
| 164 |
+
pip/_internal/network/__pycache__/auth.cpython-312.pyc,,
|
| 165 |
+
pip/_internal/network/__pycache__/cache.cpython-312.pyc,,
|
| 166 |
+
pip/_internal/network/__pycache__/download.cpython-312.pyc,,
|
| 167 |
+
pip/_internal/network/__pycache__/lazy_wheel.cpython-312.pyc,,
|
| 168 |
+
pip/_internal/network/__pycache__/session.cpython-312.pyc,,
|
| 169 |
+
pip/_internal/network/__pycache__/utils.cpython-312.pyc,,
|
| 170 |
+
pip/_internal/network/__pycache__/xmlrpc.cpython-312.pyc,,
|
| 171 |
+
pip/_internal/network/auth.py,sha256=D4gASjUrqoDFlSt6gQ767KAAjv6PUyJU0puDlhXNVRE,20809
|
| 172 |
+
pip/_internal/network/cache.py,sha256=48A971qCzKNFvkb57uGEk7-0xaqPS0HWj2711QNTxkU,3935
|
| 173 |
+
pip/_internal/network/download.py,sha256=FLOP29dPYECBiAi7eEjvAbNkyzaKNqbyjOT2m8HPW8U,6048
|
| 174 |
+
pip/_internal/network/lazy_wheel.py,sha256=PBdoMoNQQIA84Fhgne38jWF52W4x_KtsHjxgv4dkRKA,7622
|
| 175 |
+
pip/_internal/network/session.py,sha256=XmanBKjVwPFmh1iJ58q6TDh9xabH37gREuQJ_feuZGA,18741
|
| 176 |
+
pip/_internal/network/utils.py,sha256=Inaxel-NxBu4PQWkjyErdnfewsFCcgHph7dzR1-FboY,4088
|
| 177 |
+
pip/_internal/network/xmlrpc.py,sha256=sAxzOacJ-N1NXGPvap9jC3zuYWSnnv3GXtgR2-E2APA,1838
|
| 178 |
+
pip/_internal/operations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 179 |
+
pip/_internal/operations/__pycache__/__init__.cpython-312.pyc,,
|
| 180 |
+
pip/_internal/operations/__pycache__/check.cpython-312.pyc,,
|
| 181 |
+
pip/_internal/operations/__pycache__/freeze.cpython-312.pyc,,
|
| 182 |
+
pip/_internal/operations/__pycache__/prepare.cpython-312.pyc,,
|
| 183 |
+
pip/_internal/operations/build/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 184 |
+
pip/_internal/operations/build/__pycache__/__init__.cpython-312.pyc,,
|
| 185 |
+
pip/_internal/operations/build/__pycache__/build_tracker.cpython-312.pyc,,
|
| 186 |
+
pip/_internal/operations/build/__pycache__/metadata.cpython-312.pyc,,
|
| 187 |
+
pip/_internal/operations/build/__pycache__/metadata_editable.cpython-312.pyc,,
|
| 188 |
+
pip/_internal/operations/build/__pycache__/metadata_legacy.cpython-312.pyc,,
|
| 189 |
+
pip/_internal/operations/build/__pycache__/wheel.cpython-312.pyc,,
|
| 190 |
+
pip/_internal/operations/build/__pycache__/wheel_editable.cpython-312.pyc,,
|
| 191 |
+
pip/_internal/operations/build/__pycache__/wheel_legacy.cpython-312.pyc,,
|
| 192 |
+
pip/_internal/operations/build/build_tracker.py,sha256=-ARW_TcjHCOX7D2NUOGntB4Fgc6b4aolsXkAK6BWL7w,4774
|
| 193 |
+
pip/_internal/operations/build/metadata.py,sha256=9S0CUD8U3QqZeXp-Zyt8HxwU90lE4QrnYDgrqZDzBnc,1422
|
| 194 |
+
pip/_internal/operations/build/metadata_editable.py,sha256=VLL7LvntKE8qxdhUdEJhcotFzUsOSI8NNS043xULKew,1474
|
| 195 |
+
pip/_internal/operations/build/metadata_legacy.py,sha256=8i6i1QZX9m_lKPStEFsHKM0MT4a-CD408JOw99daLmo,2190
|
| 196 |
+
pip/_internal/operations/build/wheel.py,sha256=sT12FBLAxDC6wyrDorh8kvcZ1jG5qInCRWzzP-UkJiQ,1075
|
| 197 |
+
pip/_internal/operations/build/wheel_editable.py,sha256=yOtoH6zpAkoKYEUtr8FhzrYnkNHQaQBjWQ2HYae1MQg,1417
|
| 198 |
+
pip/_internal/operations/build/wheel_legacy.py,sha256=K-6kNhmj-1xDF45ny1yheMerF0ui4EoQCLzEoHh6-tc,3045
|
| 199 |
+
pip/_internal/operations/check.py,sha256=L24vRL8VWbyywdoeAhM89WCd8zLTnjIbULlKelUgIec,5912
|
| 200 |
+
pip/_internal/operations/freeze.py,sha256=V59yEyCSz_YhZuhH09-6aV_zvYBMrS_IxFFNqn2QzlA,9864
|
| 201 |
+
pip/_internal/operations/install/__init__.py,sha256=mX7hyD2GNBO2mFGokDQ30r_GXv7Y_PLdtxcUv144e-s,51
|
| 202 |
+
pip/_internal/operations/install/__pycache__/__init__.cpython-312.pyc,,
|
| 203 |
+
pip/_internal/operations/install/__pycache__/editable_legacy.cpython-312.pyc,,
|
| 204 |
+
pip/_internal/operations/install/__pycache__/wheel.cpython-312.pyc,,
|
| 205 |
+
pip/_internal/operations/install/editable_legacy.py,sha256=PoEsNEPGbIZ2yQphPsmYTKLOCMs4gv5OcCdzW124NcA,1283
|
| 206 |
+
pip/_internal/operations/install/wheel.py,sha256=X5Iz9yUg5LlK5VNQ9g2ikc6dcRu8EPi_SUi5iuEDRgo,27615
|
| 207 |
+
pip/_internal/operations/prepare.py,sha256=joWJwPkuqGscQgVNImLK71e9hRapwKvRCM8HclysmvU,28118
|
| 208 |
+
pip/_internal/pyproject.py,sha256=rw4fwlptDp1hZgYoplwbAGwWA32sWQkp7ysf8Ju6iXc,7287
|
| 209 |
+
pip/_internal/req/__init__.py,sha256=HxBFtZy_BbCclLgr26waMtpzYdO5T3vxePvpGAXSt5s,2653
|
| 210 |
+
pip/_internal/req/__pycache__/__init__.cpython-312.pyc,,
|
| 211 |
+
pip/_internal/req/__pycache__/constructors.cpython-312.pyc,,
|
| 212 |
+
pip/_internal/req/__pycache__/req_file.cpython-312.pyc,,
|
| 213 |
+
pip/_internal/req/__pycache__/req_install.cpython-312.pyc,,
|
| 214 |
+
pip/_internal/req/__pycache__/req_set.cpython-312.pyc,,
|
| 215 |
+
pip/_internal/req/__pycache__/req_uninstall.cpython-312.pyc,,
|
| 216 |
+
pip/_internal/req/constructors.py,sha256=v1qzCN1mIldwx-nCrPc8JO4lxkm3Fv8M5RWvt8LISjc,18430
|
| 217 |
+
pip/_internal/req/req_file.py,sha256=gOOJTzL-mDRPcQhjwqjDrjn4V-3rK9TnEFnU3v8RA4Q,18752
|
| 218 |
+
pip/_internal/req/req_install.py,sha256=yhT98NGDoAEk03jznTJnYCznzhiMEEA2ocgsUG_dcNU,35788
|
| 219 |
+
pip/_internal/req/req_set.py,sha256=j3esG0s6SzoVReX9rWn4rpYNtyET_fwxbwJPRimvRxo,2858
|
| 220 |
+
pip/_internal/req/req_uninstall.py,sha256=qzDIxJo-OETWqGais7tSMCDcWbATYABT-Tid3ityF0s,23853
|
| 221 |
+
pip/_internal/resolution/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 222 |
+
pip/_internal/resolution/__pycache__/__init__.cpython-312.pyc,,
|
| 223 |
+
pip/_internal/resolution/__pycache__/base.cpython-312.pyc,,
|
| 224 |
+
pip/_internal/resolution/base.py,sha256=qlmh325SBVfvG6Me9gc5Nsh5sdwHBwzHBq6aEXtKsLA,583
|
| 225 |
+
pip/_internal/resolution/legacy/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 226 |
+
pip/_internal/resolution/legacy/__pycache__/__init__.cpython-312.pyc,,
|
| 227 |
+
pip/_internal/resolution/legacy/__pycache__/resolver.cpython-312.pyc,,
|
| 228 |
+
pip/_internal/resolution/legacy/resolver.py,sha256=3HZiJBRd1FTN6jQpI4qRO8-TbLYeIbUTS6PFvXnXs2w,24068
|
| 229 |
+
pip/_internal/resolution/resolvelib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 230 |
+
pip/_internal/resolution/resolvelib/__pycache__/__init__.cpython-312.pyc,,
|
| 231 |
+
pip/_internal/resolution/resolvelib/__pycache__/base.cpython-312.pyc,,
|
| 232 |
+
pip/_internal/resolution/resolvelib/__pycache__/candidates.cpython-312.pyc,,
|
| 233 |
+
pip/_internal/resolution/resolvelib/__pycache__/factory.cpython-312.pyc,,
|
| 234 |
+
pip/_internal/resolution/resolvelib/__pycache__/found_candidates.cpython-312.pyc,,
|
| 235 |
+
pip/_internal/resolution/resolvelib/__pycache__/provider.cpython-312.pyc,,
|
| 236 |
+
pip/_internal/resolution/resolvelib/__pycache__/reporter.cpython-312.pyc,,
|
| 237 |
+
pip/_internal/resolution/resolvelib/__pycache__/requirements.cpython-312.pyc,,
|
| 238 |
+
pip/_internal/resolution/resolvelib/__pycache__/resolver.cpython-312.pyc,,
|
| 239 |
+
pip/_internal/resolution/resolvelib/base.py,sha256=DCf669FsqyQY5uqXeePDHQY1e4QO-pBzWH8O0s9-K94,5023
|
| 240 |
+
pip/_internal/resolution/resolvelib/candidates.py,sha256=5UZ1upNnmqsP-nmEZaDYxaBgCoejw_e2WVGmmAvBxXc,20001
|
| 241 |
+
pip/_internal/resolution/resolvelib/factory.py,sha256=511CaUR41LqjALuFafLVfx15WRvMhxYTdjQCoSvp4gw,32661
|
| 242 |
+
pip/_internal/resolution/resolvelib/found_candidates.py,sha256=9hrTyQqFvl9I7Tji79F1AxHv39Qh1rkJ_7deSHSMfQc,6383
|
| 243 |
+
pip/_internal/resolution/resolvelib/provider.py,sha256=bcsFnYvlmtB80cwVdW1fIwgol8ZNr1f1VHyRTkz47SM,9935
|
| 244 |
+
pip/_internal/resolution/resolvelib/reporter.py,sha256=00JtoXEkTlw0-rl_sl54d71avwOsJHt9GGHcrj5Sza0,3168
|
| 245 |
+
pip/_internal/resolution/resolvelib/requirements.py,sha256=7JG4Z72e5Yk4vU0S5ulGvbqTy4FMQGYhY5zQhX9zTtY,8065
|
| 246 |
+
pip/_internal/resolution/resolvelib/resolver.py,sha256=nLJOsVMEVi2gQUVJoUFKMZAeu2f7GRMjGMvNSWyz0Bc,12592
|
| 247 |
+
pip/_internal/self_outdated_check.py,sha256=pkjQixuWyQ1vrVxZAaYD6SSHgXuFUnHZybXEWTkh0S0,8145
|
| 248 |
+
pip/_internal/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 249 |
+
pip/_internal/utils/__pycache__/__init__.cpython-312.pyc,,
|
| 250 |
+
pip/_internal/utils/__pycache__/_jaraco_text.cpython-312.pyc,,
|
| 251 |
+
pip/_internal/utils/__pycache__/_log.cpython-312.pyc,,
|
| 252 |
+
pip/_internal/utils/__pycache__/appdirs.cpython-312.pyc,,
|
| 253 |
+
pip/_internal/utils/__pycache__/compat.cpython-312.pyc,,
|
| 254 |
+
pip/_internal/utils/__pycache__/compatibility_tags.cpython-312.pyc,,
|
| 255 |
+
pip/_internal/utils/__pycache__/datetime.cpython-312.pyc,,
|
| 256 |
+
pip/_internal/utils/__pycache__/deprecation.cpython-312.pyc,,
|
| 257 |
+
pip/_internal/utils/__pycache__/direct_url_helpers.cpython-312.pyc,,
|
| 258 |
+
pip/_internal/utils/__pycache__/egg_link.cpython-312.pyc,,
|
| 259 |
+
pip/_internal/utils/__pycache__/encoding.cpython-312.pyc,,
|
| 260 |
+
pip/_internal/utils/__pycache__/entrypoints.cpython-312.pyc,,
|
| 261 |
+
pip/_internal/utils/__pycache__/filesystem.cpython-312.pyc,,
|
| 262 |
+
pip/_internal/utils/__pycache__/filetypes.cpython-312.pyc,,
|
| 263 |
+
pip/_internal/utils/__pycache__/glibc.cpython-312.pyc,,
|
| 264 |
+
pip/_internal/utils/__pycache__/hashes.cpython-312.pyc,,
|
| 265 |
+
pip/_internal/utils/__pycache__/logging.cpython-312.pyc,,
|
| 266 |
+
pip/_internal/utils/__pycache__/misc.cpython-312.pyc,,
|
| 267 |
+
pip/_internal/utils/__pycache__/packaging.cpython-312.pyc,,
|
| 268 |
+
pip/_internal/utils/__pycache__/retry.cpython-312.pyc,,
|
| 269 |
+
pip/_internal/utils/__pycache__/setuptools_build.cpython-312.pyc,,
|
| 270 |
+
pip/_internal/utils/__pycache__/subprocess.cpython-312.pyc,,
|
| 271 |
+
pip/_internal/utils/__pycache__/temp_dir.cpython-312.pyc,,
|
| 272 |
+
pip/_internal/utils/__pycache__/unpacking.cpython-312.pyc,,
|
| 273 |
+
pip/_internal/utils/__pycache__/urls.cpython-312.pyc,,
|
| 274 |
+
pip/_internal/utils/__pycache__/virtualenv.cpython-312.pyc,,
|
| 275 |
+
pip/_internal/utils/__pycache__/wheel.cpython-312.pyc,,
|
| 276 |
+
pip/_internal/utils/_jaraco_text.py,sha256=M15uUPIh5NpP1tdUGBxRau6q1ZAEtI8-XyLEETscFfE,3350
|
| 277 |
+
pip/_internal/utils/_log.py,sha256=-jHLOE_THaZz5BFcCnoSL9EYAtJ0nXem49s9of4jvKw,1015
|
| 278 |
+
pip/_internal/utils/appdirs.py,sha256=swgcTKOm3daLeXTW6v5BUS2Ti2RvEnGRQYH_yDXklAo,1665
|
| 279 |
+
pip/_internal/utils/compat.py,sha256=ckkFveBiYQjRWjkNsajt_oWPS57tJvE8XxoC4OIYgCY,2399
|
| 280 |
+
pip/_internal/utils/compatibility_tags.py,sha256=OWq5axHpW-MEEPztGdvgADrgJPAcV9a88Rxm4Z8VBs8,6272
|
| 281 |
+
pip/_internal/utils/datetime.py,sha256=m21Y3wAtQc-ji6Veb6k_M5g6A0ZyFI4egchTdnwh-pQ,242
|
| 282 |
+
pip/_internal/utils/deprecation.py,sha256=k7Qg_UBAaaTdyq82YVARA6D7RmcGTXGv7fnfcgigj4Q,3707
|
| 283 |
+
pip/_internal/utils/direct_url_helpers.py,sha256=r2MRtkVDACv9AGqYODBUC9CjwgtsUU1s68hmgfCJMtA,3196
|
| 284 |
+
pip/_internal/utils/egg_link.py,sha256=0FePZoUYKv4RGQ2t6x7w5Z427wbA_Uo3WZnAkrgsuqo,2463
|
| 285 |
+
pip/_internal/utils/encoding.py,sha256=qqsXDtiwMIjXMEiIVSaOjwH5YmirCaK-dIzb6-XJsL0,1169
|
| 286 |
+
pip/_internal/utils/entrypoints.py,sha256=YlhLTRl2oHBAuqhc-zmL7USS67TPWVHImjeAQHreZTQ,3064
|
| 287 |
+
pip/_internal/utils/filesystem.py,sha256=ajvA-q4ocliW9kPp8Yquh-4vssXbu-UKbo5FV9V4X64,4950
|
| 288 |
+
pip/_internal/utils/filetypes.py,sha256=i8XAQ0eFCog26Fw9yV0Yb1ygAqKYB1w9Cz9n0fj8gZU,716
|
| 289 |
+
pip/_internal/utils/glibc.py,sha256=vUkWq_1pJuzcYNcGKLlQmABoUiisK8noYY1yc8Wq4w4,3734
|
| 290 |
+
pip/_internal/utils/hashes.py,sha256=XGGLL0AG8-RhWnyz87xF6MFZ--BKadHU35D47eApCKI,4972
|
| 291 |
+
pip/_internal/utils/logging.py,sha256=7BFKB1uFjdxD5crM-GtwA5T2qjbQ2LPD-gJDuJeDNTg,11606
|
| 292 |
+
pip/_internal/utils/misc.py,sha256=NRV0_2fFhzy1jhvInSBv4dqCmTwct8PV7Kp0m-BPRGM,23530
|
| 293 |
+
pip/_internal/utils/packaging.py,sha256=iI3LH43lVNR4hWBOqF6lFsZq4aycb2j0UcHlmDmcqUg,2109
|
| 294 |
+
pip/_internal/utils/retry.py,sha256=mhFbykXjhTnZfgzeuy-vl9c8nECnYn_CMtwNJX2tYzQ,1392
|
| 295 |
+
pip/_internal/utils/setuptools_build.py,sha256=ouXpud-jeS8xPyTPsXJ-m34NPvK5os45otAzdSV_IJE,4435
|
| 296 |
+
pip/_internal/utils/subprocess.py,sha256=EsvqSRiSMHF98T8Txmu6NLU3U--MpTTQjtNgKP0P--M,8988
|
| 297 |
+
pip/_internal/utils/temp_dir.py,sha256=5qOXe8M4JeY6vaFQM867d5zkp1bSwMZ-KT5jymmP0Zg,9310
|
| 298 |
+
pip/_internal/utils/unpacking.py,sha256=eyDkSsk4nW8ZfiSjNzJduCznpHyaGHVv3ak_LMGsiEM,11951
|
| 299 |
+
pip/_internal/utils/urls.py,sha256=qceSOZb5lbNDrHNsv7_S4L4Ytszja5NwPKUMnZHbYnM,1599
|
| 300 |
+
pip/_internal/utils/virtualenv.py,sha256=S6f7csYorRpiD6cvn3jISZYc3I8PJC43H5iMFpRAEDU,3456
|
| 301 |
+
pip/_internal/utils/wheel.py,sha256=b442jkydFHjXzDy6cMR7MpzWBJ1Q82hR5F33cmcHV3g,4494
|
| 302 |
+
pip/_internal/vcs/__init__.py,sha256=UAqvzpbi0VbZo3Ub6skEeZAw-ooIZR-zX_WpCbxyCoU,596
|
| 303 |
+
pip/_internal/vcs/__pycache__/__init__.cpython-312.pyc,,
|
| 304 |
+
pip/_internal/vcs/__pycache__/bazaar.cpython-312.pyc,,
|
| 305 |
+
pip/_internal/vcs/__pycache__/git.cpython-312.pyc,,
|
| 306 |
+
pip/_internal/vcs/__pycache__/mercurial.cpython-312.pyc,,
|
| 307 |
+
pip/_internal/vcs/__pycache__/subversion.cpython-312.pyc,,
|
| 308 |
+
pip/_internal/vcs/__pycache__/versioncontrol.cpython-312.pyc,,
|
| 309 |
+
pip/_internal/vcs/bazaar.py,sha256=EKStcQaKpNu0NK4p5Q10Oc4xb3DUxFw024XrJy40bFQ,3528
|
| 310 |
+
pip/_internal/vcs/git.py,sha256=3tpc9LQA_J4IVW5r5NvWaaSeDzcmJOrSFZN0J8vIKfU,18177
|
| 311 |
+
pip/_internal/vcs/mercurial.py,sha256=oULOhzJ2Uie-06d1omkL-_Gc6meGaUkyogvqG9ZCyPs,5249
|
| 312 |
+
pip/_internal/vcs/subversion.py,sha256=ddTugHBqHzV3ebKlU5QXHPN4gUqlyXbOx8q8NgXKvs8,11735
|
| 313 |
+
pip/_internal/vcs/versioncontrol.py,sha256=cvf_-hnTAjQLXJ3d17FMNhQfcO1AcKWUF10tfrYyP-c,22440
|
| 314 |
+
pip/_internal/wheel_builder.py,sha256=DL3A8LKeRj_ACp11WS5wSgASgPFqeyAeXJKdXfmaWXU,11799
|
| 315 |
+
pip/_vendor/__init__.py,sha256=JYuAXvClhInxIrA2FTp5p-uuWVL7WV6-vEpTs46-Qh4,4873
|
| 316 |
+
pip/_vendor/__pycache__/__init__.cpython-312.pyc,,
|
| 317 |
+
pip/_vendor/__pycache__/typing_extensions.cpython-312.pyc,,
|
| 318 |
+
pip/_vendor/cachecontrol/__init__.py,sha256=GiYoagwPEiJ_xR_lbwWGaoCiPtF_rz4isjfjdDAgHU4,676
|
| 319 |
+
pip/_vendor/cachecontrol/__pycache__/__init__.cpython-312.pyc,,
|
| 320 |
+
pip/_vendor/cachecontrol/__pycache__/_cmd.cpython-312.pyc,,
|
| 321 |
+
pip/_vendor/cachecontrol/__pycache__/adapter.cpython-312.pyc,,
|
| 322 |
+
pip/_vendor/cachecontrol/__pycache__/cache.cpython-312.pyc,,
|
| 323 |
+
pip/_vendor/cachecontrol/__pycache__/controller.cpython-312.pyc,,
|
| 324 |
+
pip/_vendor/cachecontrol/__pycache__/filewrapper.cpython-312.pyc,,
|
| 325 |
+
pip/_vendor/cachecontrol/__pycache__/heuristics.cpython-312.pyc,,
|
| 326 |
+
pip/_vendor/cachecontrol/__pycache__/serialize.cpython-312.pyc,,
|
| 327 |
+
pip/_vendor/cachecontrol/__pycache__/wrapper.cpython-312.pyc,,
|
| 328 |
+
pip/_vendor/cachecontrol/_cmd.py,sha256=iist2EpzJvDVIhMAxXq8iFnTBsiZAd6iplxfmNboNyk,1737
|
| 329 |
+
pip/_vendor/cachecontrol/adapter.py,sha256=fByO_Pd_EOemjWbuocvBWdN85xT0q_TBm2lxS6vD4fk,6355
|
| 330 |
+
pip/_vendor/cachecontrol/cache.py,sha256=OTQj72tUf8C1uEgczdl3Gc8vkldSzsTITKtDGKMx4z8,1952
|
| 331 |
+
pip/_vendor/cachecontrol/caches/__init__.py,sha256=dtrrroK5BnADR1GWjCZ19aZ0tFsMfvFBtLQQU1sp_ag,303
|
| 332 |
+
pip/_vendor/cachecontrol/caches/__pycache__/__init__.cpython-312.pyc,,
|
| 333 |
+
pip/_vendor/cachecontrol/caches/__pycache__/file_cache.cpython-312.pyc,,
|
| 334 |
+
pip/_vendor/cachecontrol/caches/__pycache__/redis_cache.cpython-312.pyc,,
|
| 335 |
+
pip/_vendor/cachecontrol/caches/file_cache.py,sha256=9AlmmTJc6cslb6k5z_6q0sGPHVrMj8zv-uWy-simmfE,5406
|
| 336 |
+
pip/_vendor/cachecontrol/caches/redis_cache.py,sha256=9rmqwtYu_ljVkW6_oLqbC7EaX_a8YT_yLuna-eS0dgo,1386
|
| 337 |
+
pip/_vendor/cachecontrol/controller.py,sha256=o-ejGJlBmpKK8QQLyTPJj0t7siU8XVHXuV8MCybCxQ8,18575
|
| 338 |
+
pip/_vendor/cachecontrol/filewrapper.py,sha256=STttGmIPBvZzt2b51dUOwoWX5crcMCpKZOisM3f5BNc,4292
|
| 339 |
+
pip/_vendor/cachecontrol/heuristics.py,sha256=IYe4QmHERWsMvtxNrp920WeaIsaTTyqLB14DSheSbtY,4834
|
| 340 |
+
pip/_vendor/cachecontrol/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 341 |
+
pip/_vendor/cachecontrol/serialize.py,sha256=HQd2IllQ05HzPkVLMXTF2uX5mjEQjDBkxCqUJUODpZk,5163
|
| 342 |
+
pip/_vendor/cachecontrol/wrapper.py,sha256=hsGc7g8QGQTT-4f8tgz3AM5qwScg6FO0BSdLSRdEvpU,1417
|
| 343 |
+
pip/_vendor/certifi/__init__.py,sha256=p_GYZrjUwPBUhpLlCZoGb0miKBKSqDAyZC5DvIuqbHQ,94
|
| 344 |
+
pip/_vendor/certifi/__main__.py,sha256=1k3Cr95vCxxGRGDljrW3wMdpZdL3Nhf0u1n-k2qdsCY,255
|
| 345 |
+
pip/_vendor/certifi/__pycache__/__init__.cpython-312.pyc,,
|
| 346 |
+
pip/_vendor/certifi/__pycache__/__main__.cpython-312.pyc,,
|
| 347 |
+
pip/_vendor/certifi/__pycache__/core.cpython-312.pyc,,
|
| 348 |
+
pip/_vendor/certifi/cacert.pem,sha256=lO3rZukXdPyuk6BWUJFOKQliWaXH6HGh9l1GGrUgG0c,299427
|
| 349 |
+
pip/_vendor/certifi/core.py,sha256=2SRT5rIcQChFDbe37BQa-kULxAgJ8qN6l1jfqTp4HIs,4486
|
| 350 |
+
pip/_vendor/certifi/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 351 |
+
pip/_vendor/distlib/__init__.py,sha256=dcwgYGYGQqAEawBXPDtIx80DO_3cOmFv8HTc8JMzknQ,625
|
| 352 |
+
pip/_vendor/distlib/__pycache__/__init__.cpython-312.pyc,,
|
| 353 |
+
pip/_vendor/distlib/__pycache__/compat.cpython-312.pyc,,
|
| 354 |
+
pip/_vendor/distlib/__pycache__/database.cpython-312.pyc,,
|
| 355 |
+
pip/_vendor/distlib/__pycache__/index.cpython-312.pyc,,
|
| 356 |
+
pip/_vendor/distlib/__pycache__/locators.cpython-312.pyc,,
|
| 357 |
+
pip/_vendor/distlib/__pycache__/manifest.cpython-312.pyc,,
|
| 358 |
+
pip/_vendor/distlib/__pycache__/markers.cpython-312.pyc,,
|
| 359 |
+
pip/_vendor/distlib/__pycache__/metadata.cpython-312.pyc,,
|
| 360 |
+
pip/_vendor/distlib/__pycache__/resources.cpython-312.pyc,,
|
| 361 |
+
pip/_vendor/distlib/__pycache__/scripts.cpython-312.pyc,,
|
| 362 |
+
pip/_vendor/distlib/__pycache__/util.cpython-312.pyc,,
|
| 363 |
+
pip/_vendor/distlib/__pycache__/version.cpython-312.pyc,,
|
| 364 |
+
pip/_vendor/distlib/__pycache__/wheel.cpython-312.pyc,,
|
| 365 |
+
pip/_vendor/distlib/compat.py,sha256=2jRSjRI4o-vlXeTK2BCGIUhkc6e9ZGhSsacRM5oseTw,41467
|
| 366 |
+
pip/_vendor/distlib/database.py,sha256=mHy_LxiXIsIVRb-T0-idBrVLw3Ffij5teHCpbjmJ9YU,51160
|
| 367 |
+
pip/_vendor/distlib/index.py,sha256=lTbw268rRhj8dw1sib3VZ_0EhSGgoJO3FKJzSFMOaeA,20797
|
| 368 |
+
pip/_vendor/distlib/locators.py,sha256=oBeAZpFuPQSY09MgNnLfQGGAXXvVO96BFpZyKMuK4tM,51026
|
| 369 |
+
pip/_vendor/distlib/manifest.py,sha256=3qfmAmVwxRqU1o23AlfXrQGZzh6g_GGzTAP_Hb9C5zQ,14168
|
| 370 |
+
pip/_vendor/distlib/markers.py,sha256=X6sDvkFGcYS8gUW8hfsWuKEKAqhQZAJ7iXOMLxRYjYk,5164
|
| 371 |
+
pip/_vendor/distlib/metadata.py,sha256=zil3sg2EUfLXVigljY2d_03IJt-JSs7nX-73fECMX2s,38724
|
| 372 |
+
pip/_vendor/distlib/resources.py,sha256=LwbPksc0A1JMbi6XnuPdMBUn83X7BPuFNWqPGEKI698,10820
|
| 373 |
+
pip/_vendor/distlib/scripts.py,sha256=BJliaDAZaVB7WAkwokgC3HXwLD2iWiHaVI50H7C6eG8,18608
|
| 374 |
+
pip/_vendor/distlib/t32.exe,sha256=a0GV5kCoWsMutvliiCKmIgV98eRZ33wXoS-XrqvJQVs,97792
|
| 375 |
+
pip/_vendor/distlib/t64-arm.exe,sha256=68TAa32V504xVBnufojh0PcenpR3U4wAqTqf-MZqbPw,182784
|
| 376 |
+
pip/_vendor/distlib/t64.exe,sha256=gaYY8hy4fbkHYTTnA4i26ct8IQZzkBG2pRdy0iyuBrc,108032
|
| 377 |
+
pip/_vendor/distlib/util.py,sha256=vMPGvsS4j9hF6Y9k3Tyom1aaHLb0rFmZAEyzeAdel9w,66682
|
| 378 |
+
pip/_vendor/distlib/version.py,sha256=s5VIs8wBn0fxzGxWM_aA2ZZyx525HcZbMvcTlTyZ3Rg,23727
|
| 379 |
+
pip/_vendor/distlib/w32.exe,sha256=R4csx3-OGM9kL4aPIzQKRo5TfmRSHZo6QWyLhDhNBks,91648
|
| 380 |
+
pip/_vendor/distlib/w64-arm.exe,sha256=xdyYhKj0WDcVUOCb05blQYvzdYIKMbmJn2SZvzkcey4,168448
|
| 381 |
+
pip/_vendor/distlib/w64.exe,sha256=ejGf-rojoBfXseGLpya6bFTFPWRG21X5KvU8J5iU-K0,101888
|
| 382 |
+
pip/_vendor/distlib/wheel.py,sha256=DFIVguEQHCdxnSdAO0dfFsgMcvVZitg7bCOuLwZ7A_s,43979
|
| 383 |
+
pip/_vendor/distro/__init__.py,sha256=2fHjF-SfgPvjyNZ1iHh_wjqWdR_Yo5ODHwZC0jLBPhc,981
|
| 384 |
+
pip/_vendor/distro/__main__.py,sha256=bu9d3TifoKciZFcqRBuygV3GSuThnVD_m2IK4cz96Vs,64
|
| 385 |
+
pip/_vendor/distro/__pycache__/__init__.cpython-312.pyc,,
|
| 386 |
+
pip/_vendor/distro/__pycache__/__main__.cpython-312.pyc,,
|
| 387 |
+
pip/_vendor/distro/__pycache__/distro.cpython-312.pyc,,
|
| 388 |
+
pip/_vendor/distro/distro.py,sha256=XqbefacAhDT4zr_trnbA15eY8vdK4GTghgmvUGrEM_4,49430
|
| 389 |
+
pip/_vendor/distro/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 390 |
+
pip/_vendor/idna/__init__.py,sha256=KJQN1eQBr8iIK5SKrJ47lXvxG0BJ7Lm38W4zT0v_8lk,849
|
| 391 |
+
pip/_vendor/idna/__pycache__/__init__.cpython-312.pyc,,
|
| 392 |
+
pip/_vendor/idna/__pycache__/codec.cpython-312.pyc,,
|
| 393 |
+
pip/_vendor/idna/__pycache__/compat.cpython-312.pyc,,
|
| 394 |
+
pip/_vendor/idna/__pycache__/core.cpython-312.pyc,,
|
| 395 |
+
pip/_vendor/idna/__pycache__/idnadata.cpython-312.pyc,,
|
| 396 |
+
pip/_vendor/idna/__pycache__/intranges.cpython-312.pyc,,
|
| 397 |
+
pip/_vendor/idna/__pycache__/package_data.cpython-312.pyc,,
|
| 398 |
+
pip/_vendor/idna/__pycache__/uts46data.cpython-312.pyc,,
|
| 399 |
+
pip/_vendor/idna/codec.py,sha256=PS6m-XmdST7Wj7J7ulRMakPDt5EBJyYrT3CPtjh-7t4,3426
|
| 400 |
+
pip/_vendor/idna/compat.py,sha256=0_sOEUMT4CVw9doD3vyRhX80X19PwqFoUBs7gWsFME4,321
|
| 401 |
+
pip/_vendor/idna/core.py,sha256=lyhpoe2vulEaB_65xhXmoKgO-xUqFDvcwxu5hpNNO4E,12663
|
| 402 |
+
pip/_vendor/idna/idnadata.py,sha256=dqRwytzkjIHMBa2R1lYvHDwACenZPt8eGVu1Y8UBE-E,78320
|
| 403 |
+
pip/_vendor/idna/intranges.py,sha256=YBr4fRYuWH7kTKS2tXlFjM24ZF1Pdvcir-aywniInqg,1881
|
| 404 |
+
pip/_vendor/idna/package_data.py,sha256=Tkt0KnIeyIlnHddOaz9WSkkislNgokJAuE-p5GorMqo,21
|
| 405 |
+
pip/_vendor/idna/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 406 |
+
pip/_vendor/idna/uts46data.py,sha256=1KuksWqLuccPXm2uyRVkhfiFLNIhM_H2m4azCcnOqEU,206503
|
| 407 |
+
pip/_vendor/msgpack/__init__.py,sha256=gsMP7JTECZNUSjvOyIbdhNOkpB9Z8BcGwabVGY2UcdQ,1077
|
| 408 |
+
pip/_vendor/msgpack/__pycache__/__init__.cpython-312.pyc,,
|
| 409 |
+
pip/_vendor/msgpack/__pycache__/exceptions.cpython-312.pyc,,
|
| 410 |
+
pip/_vendor/msgpack/__pycache__/ext.cpython-312.pyc,,
|
| 411 |
+
pip/_vendor/msgpack/__pycache__/fallback.cpython-312.pyc,,
|
| 412 |
+
pip/_vendor/msgpack/exceptions.py,sha256=dCTWei8dpkrMsQDcjQk74ATl9HsIBH0ybt8zOPNqMYc,1081
|
| 413 |
+
pip/_vendor/msgpack/ext.py,sha256=fKp00BqDLjUtZnPd70Llr138zk8JsCuSpJkkZ5S4dt8,5629
|
| 414 |
+
pip/_vendor/msgpack/fallback.py,sha256=wdUWJkWX2gzfRW9BBCTOuIE1Wvrf5PtBtR8ZtY7G_EE,33175
|
| 415 |
+
pip/_vendor/packaging/__init__.py,sha256=dtw2bNmWCQ9WnMoK3bk_elL1svSlikXtLpZhCFIB9SE,496
|
| 416 |
+
pip/_vendor/packaging/__pycache__/__init__.cpython-312.pyc,,
|
| 417 |
+
pip/_vendor/packaging/__pycache__/_elffile.cpython-312.pyc,,
|
| 418 |
+
pip/_vendor/packaging/__pycache__/_manylinux.cpython-312.pyc,,
|
| 419 |
+
pip/_vendor/packaging/__pycache__/_musllinux.cpython-312.pyc,,
|
| 420 |
+
pip/_vendor/packaging/__pycache__/_parser.cpython-312.pyc,,
|
| 421 |
+
pip/_vendor/packaging/__pycache__/_structures.cpython-312.pyc,,
|
| 422 |
+
pip/_vendor/packaging/__pycache__/_tokenizer.cpython-312.pyc,,
|
| 423 |
+
pip/_vendor/packaging/__pycache__/markers.cpython-312.pyc,,
|
| 424 |
+
pip/_vendor/packaging/__pycache__/metadata.cpython-312.pyc,,
|
| 425 |
+
pip/_vendor/packaging/__pycache__/requirements.cpython-312.pyc,,
|
| 426 |
+
pip/_vendor/packaging/__pycache__/specifiers.cpython-312.pyc,,
|
| 427 |
+
pip/_vendor/packaging/__pycache__/tags.cpython-312.pyc,,
|
| 428 |
+
pip/_vendor/packaging/__pycache__/utils.cpython-312.pyc,,
|
| 429 |
+
pip/_vendor/packaging/__pycache__/version.cpython-312.pyc,,
|
| 430 |
+
pip/_vendor/packaging/_elffile.py,sha256=_LcJW4YNKywYsl4169B2ukKRqwxjxst_8H0FRVQKlz8,3282
|
| 431 |
+
pip/_vendor/packaging/_manylinux.py,sha256=Xo4V0PZz8sbuVCbTni0t1CR0AHeir_7ib4lTmV8scD4,9586
|
| 432 |
+
pip/_vendor/packaging/_musllinux.py,sha256=p9ZqNYiOItGee8KcZFeHF_YcdhVwGHdK6r-8lgixvGQ,2694
|
| 433 |
+
pip/_vendor/packaging/_parser.py,sha256=s_TvTvDNK0NrM2QB3VKThdWFM4Nc0P6JnkObkl3MjpM,10236
|
| 434 |
+
pip/_vendor/packaging/_structures.py,sha256=q3eVNmbWJGG_S0Dit_S3Ao8qQqz_5PYTXFAKBZe5yr4,1431
|
| 435 |
+
pip/_vendor/packaging/_tokenizer.py,sha256=J6v5H7Jzvb-g81xp_2QACKwO7LxHQA6ikryMU7zXwN8,5273
|
| 436 |
+
pip/_vendor/packaging/markers.py,sha256=dWKSqn5Sp-jDmOG-W3GfLHKjwhf1IsznbT71VlBoB5M,10671
|
| 437 |
+
pip/_vendor/packaging/metadata.py,sha256=KINuSkJ12u-SyoKNTy_pHNGAfMUtxNvZ53qA1zAKcKI,32349
|
| 438 |
+
pip/_vendor/packaging/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 439 |
+
pip/_vendor/packaging/requirements.py,sha256=gYyRSAdbrIyKDY66ugIDUQjRMvxkH2ALioTmX3tnL6o,2947
|
| 440 |
+
pip/_vendor/packaging/specifiers.py,sha256=HfGgfNJRvrzC759gnnoojHyiWs_DYmcw5PEh5jHH-YE,39738
|
| 441 |
+
pip/_vendor/packaging/tags.py,sha256=Fo6_cit95-7QfcMb16XtI7AUiSMgdwA_hCO_9lV2pz4,21388
|
| 442 |
+
pip/_vendor/packaging/utils.py,sha256=NAdYUwnlAOpkat_RthavX8a07YuVxgGL_vwrx73GSDM,5287
|
| 443 |
+
pip/_vendor/packaging/version.py,sha256=wE4sSVlF-d1H6HFC1vszEe35CwTig_fh4HHIFg95hFE,16210
|
| 444 |
+
pip/_vendor/pkg_resources/__init__.py,sha256=jrhDRbOubP74QuPXxd7U7Po42PH2l-LZ2XfcO7llpZ4,124463
|
| 445 |
+
pip/_vendor/pkg_resources/__pycache__/__init__.cpython-312.pyc,,
|
| 446 |
+
pip/_vendor/platformdirs/__init__.py,sha256=FTA6LGNm40GwNZt3gG3uLAacWvf2E_2HTmH0rAALGR8,22285
|
| 447 |
+
pip/_vendor/platformdirs/__main__.py,sha256=jBJ8zb7Mpx5ebcqF83xrpO94MaeCpNGHVf9cvDN2JLg,1505
|
| 448 |
+
pip/_vendor/platformdirs/__pycache__/__init__.cpython-312.pyc,,
|
| 449 |
+
pip/_vendor/platformdirs/__pycache__/__main__.cpython-312.pyc,,
|
| 450 |
+
pip/_vendor/platformdirs/__pycache__/android.cpython-312.pyc,,
|
| 451 |
+
pip/_vendor/platformdirs/__pycache__/api.cpython-312.pyc,,
|
| 452 |
+
pip/_vendor/platformdirs/__pycache__/macos.cpython-312.pyc,,
|
| 453 |
+
pip/_vendor/platformdirs/__pycache__/unix.cpython-312.pyc,,
|
| 454 |
+
pip/_vendor/platformdirs/__pycache__/version.cpython-312.pyc,,
|
| 455 |
+
pip/_vendor/platformdirs/__pycache__/windows.cpython-312.pyc,,
|
| 456 |
+
pip/_vendor/platformdirs/android.py,sha256=xZXY9Jd46WOsxT2U6-5HsNtDZ-IQqxcEUrBLl3hYk4o,9016
|
| 457 |
+
pip/_vendor/platformdirs/api.py,sha256=QBYdUac2eC521ek_y53uD1Dcq-lJX8IgSRVd4InC6uc,8996
|
| 458 |
+
pip/_vendor/platformdirs/macos.py,sha256=wftsbsvq6nZ0WORXSiCrZNkRHz_WKuktl0a6mC7MFkI,5580
|
| 459 |
+
pip/_vendor/platformdirs/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 460 |
+
pip/_vendor/platformdirs/unix.py,sha256=Cci9Wqt35dAMsg6HT9nRGHSBW5obb0pR3AE1JJnsCXg,10643
|
| 461 |
+
pip/_vendor/platformdirs/version.py,sha256=r7F76tZRjgQKzrpx_I0_ZMQOMU-PS7eGnHD7zEK3KB0,411
|
| 462 |
+
pip/_vendor/platformdirs/windows.py,sha256=IFpiohUBwxPtCzlyKwNtxyW4Jk8haa6W8o59mfrDXVo,10125
|
| 463 |
+
pip/_vendor/pygments/__init__.py,sha256=7N1oiaWulw_nCsTY4EEixYLz15pWY5u4uPAFFi-ielU,2983
|
| 464 |
+
pip/_vendor/pygments/__main__.py,sha256=isIhBxLg65nLlXukG4VkMuPfNdd7gFzTZ_R_z3Q8diY,353
|
| 465 |
+
pip/_vendor/pygments/__pycache__/__init__.cpython-312.pyc,,
|
| 466 |
+
pip/_vendor/pygments/__pycache__/__main__.cpython-312.pyc,,
|
| 467 |
+
pip/_vendor/pygments/__pycache__/cmdline.cpython-312.pyc,,
|
| 468 |
+
pip/_vendor/pygments/__pycache__/console.cpython-312.pyc,,
|
| 469 |
+
pip/_vendor/pygments/__pycache__/filter.cpython-312.pyc,,
|
| 470 |
+
pip/_vendor/pygments/__pycache__/formatter.cpython-312.pyc,,
|
| 471 |
+
pip/_vendor/pygments/__pycache__/lexer.cpython-312.pyc,,
|
| 472 |
+
pip/_vendor/pygments/__pycache__/modeline.cpython-312.pyc,,
|
| 473 |
+
pip/_vendor/pygments/__pycache__/plugin.cpython-312.pyc,,
|
| 474 |
+
pip/_vendor/pygments/__pycache__/regexopt.cpython-312.pyc,,
|
| 475 |
+
pip/_vendor/pygments/__pycache__/scanner.cpython-312.pyc,,
|
| 476 |
+
pip/_vendor/pygments/__pycache__/sphinxext.cpython-312.pyc,,
|
| 477 |
+
pip/_vendor/pygments/__pycache__/style.cpython-312.pyc,,
|
| 478 |
+
pip/_vendor/pygments/__pycache__/token.cpython-312.pyc,,
|
| 479 |
+
pip/_vendor/pygments/__pycache__/unistring.cpython-312.pyc,,
|
| 480 |
+
pip/_vendor/pygments/__pycache__/util.cpython-312.pyc,,
|
| 481 |
+
pip/_vendor/pygments/cmdline.py,sha256=LIVzmAunlk9sRJJp54O4KRy9GDIN4Wu13v9p9QzfGPM,23656
|
| 482 |
+
pip/_vendor/pygments/console.py,sha256=yhP9UsLAVmWKVQf2446JJewkA7AiXeeTf4Ieg3Oi2fU,1718
|
| 483 |
+
pip/_vendor/pygments/filter.py,sha256=_ADNPCskD8_GmodHi6_LoVgPU3Zh336aBCT5cOeTMs0,1910
|
| 484 |
+
pip/_vendor/pygments/filters/__init__.py,sha256=RdedK2KWKXlKwR7cvkfr3NUj9YiZQgMgilRMFUg2jPA,40392
|
| 485 |
+
pip/_vendor/pygments/filters/__pycache__/__init__.cpython-312.pyc,,
|
| 486 |
+
pip/_vendor/pygments/formatter.py,sha256=jDWBTndlBH2Z5IYZFVDnP0qn1CaTQjTWt7iAGtCnJEg,4390
|
| 487 |
+
pip/_vendor/pygments/formatters/__init__.py,sha256=8No-NUs8rBTSSBJIv4hSEQt2M0cFB4hwAT0snVc2QGE,5385
|
| 488 |
+
pip/_vendor/pygments/formatters/__pycache__/__init__.cpython-312.pyc,,
|
| 489 |
+
pip/_vendor/pygments/formatters/__pycache__/_mapping.cpython-312.pyc,,
|
| 490 |
+
pip/_vendor/pygments/formatters/__pycache__/bbcode.cpython-312.pyc,,
|
| 491 |
+
pip/_vendor/pygments/formatters/__pycache__/groff.cpython-312.pyc,,
|
| 492 |
+
pip/_vendor/pygments/formatters/__pycache__/html.cpython-312.pyc,,
|
| 493 |
+
pip/_vendor/pygments/formatters/__pycache__/img.cpython-312.pyc,,
|
| 494 |
+
pip/_vendor/pygments/formatters/__pycache__/irc.cpython-312.pyc,,
|
| 495 |
+
pip/_vendor/pygments/formatters/__pycache__/latex.cpython-312.pyc,,
|
| 496 |
+
pip/_vendor/pygments/formatters/__pycache__/other.cpython-312.pyc,,
|
| 497 |
+
pip/_vendor/pygments/formatters/__pycache__/pangomarkup.cpython-312.pyc,,
|
| 498 |
+
pip/_vendor/pygments/formatters/__pycache__/rtf.cpython-312.pyc,,
|
| 499 |
+
pip/_vendor/pygments/formatters/__pycache__/svg.cpython-312.pyc,,
|
| 500 |
+
pip/_vendor/pygments/formatters/__pycache__/terminal.cpython-312.pyc,,
|
| 501 |
+
pip/_vendor/pygments/formatters/__pycache__/terminal256.cpython-312.pyc,,
|
| 502 |
+
pip/_vendor/pygments/formatters/_mapping.py,sha256=1Cw37FuQlNacnxRKmtlPX4nyLoX9_ttko5ZwscNUZZ4,4176
|
| 503 |
+
pip/_vendor/pygments/formatters/bbcode.py,sha256=3JQLI45tcrQ_kRUMjuab6C7Hb0XUsbVWqqbSn9cMjkI,3320
|
| 504 |
+
pip/_vendor/pygments/formatters/groff.py,sha256=M39k0PaSSZRnxWjqBSVPkF0mu1-Vr7bm6RsFvs-CNN4,5106
|
| 505 |
+
pip/_vendor/pygments/formatters/html.py,sha256=SE2jc3YCqbMS3rZW9EAmDlAUhdVxJ52gA4dileEvCGU,35669
|
| 506 |
+
pip/_vendor/pygments/formatters/img.py,sha256=MwA4xWPLOwh6j7Yc6oHzjuqSPt0M1fh5r-5BTIIUfsU,23287
|
| 507 |
+
pip/_vendor/pygments/formatters/irc.py,sha256=dp1Z0l_ObJ5NFh9MhqLGg5ptG5hgJqedT2Vkutt9v0M,4981
|
| 508 |
+
pip/_vendor/pygments/formatters/latex.py,sha256=XMmhOCqUKDBQtG5mGJNAFYxApqaC5puo5cMmPfK3944,19306
|
| 509 |
+
pip/_vendor/pygments/formatters/other.py,sha256=56PMJOliin-rAUdnRM0i1wsV1GdUPd_dvQq0_UPfF9c,5034
|
| 510 |
+
pip/_vendor/pygments/formatters/pangomarkup.py,sha256=y16U00aVYYEFpeCfGXlYBSMacG425CbfoG8oKbKegIg,2218
|
| 511 |
+
pip/_vendor/pygments/formatters/rtf.py,sha256=ZT90dmcKyJboIB0mArhL7IhE467GXRN0G7QAUgG03To,11957
|
| 512 |
+
pip/_vendor/pygments/formatters/svg.py,sha256=KKsiophPupHuxm0So-MsbQEWOT54IAiSF7hZPmxtKXE,7174
|
| 513 |
+
pip/_vendor/pygments/formatters/terminal.py,sha256=AojNG4MlKq2L6IsC_VnXHu4AbHCBn9Otog6u45XvxeI,4674
|
| 514 |
+
pip/_vendor/pygments/formatters/terminal256.py,sha256=kGkNUVo3FpwjytIDS0if79EuUoroAprcWt3igrcIqT0,11753
|
| 515 |
+
pip/_vendor/pygments/lexer.py,sha256=TYHDt___gNW4axTl2zvPZff-VQi8fPaIh5OKRcVSjUM,35349
|
| 516 |
+
pip/_vendor/pygments/lexers/__init__.py,sha256=pIlxyQJuu_syh9lE080cq8ceVbEVcKp0osAFU5fawJU,12115
|
| 517 |
+
pip/_vendor/pygments/lexers/__pycache__/__init__.cpython-312.pyc,,
|
| 518 |
+
pip/_vendor/pygments/lexers/__pycache__/_mapping.cpython-312.pyc,,
|
| 519 |
+
pip/_vendor/pygments/lexers/__pycache__/python.cpython-312.pyc,,
|
| 520 |
+
pip/_vendor/pygments/lexers/_mapping.py,sha256=61-h3zr103m01OS5BUq_AfUiL9YI06Ves9ipQ7k4vr4,76097
|
| 521 |
+
pip/_vendor/pygments/lexers/python.py,sha256=2J_YJrPTr_A6fJY_qKiKv0GpgPwHMrlMSeo59qN3fe4,53687
|
| 522 |
+
pip/_vendor/pygments/modeline.py,sha256=gtRYZBS-CKOCDXHhGZqApboHBaZwGH8gznN3O6nuxj4,1005
|
| 523 |
+
pip/_vendor/pygments/plugin.py,sha256=ioeJ3QeoJ-UQhZpY9JL7vbxsTVuwwM7BCu-Jb8nN0AU,1891
|
| 524 |
+
pip/_vendor/pygments/regexopt.py,sha256=Hky4EB13rIXEHQUNkwmCrYqtIlnXDehNR3MztafZ43w,3072
|
| 525 |
+
pip/_vendor/pygments/scanner.py,sha256=NDy3ofK_fHRFK4hIDvxpamG871aewqcsIb6sgTi7Fhk,3092
|
| 526 |
+
pip/_vendor/pygments/sphinxext.py,sha256=iOptJBcqOGPwMEJ2p70PvwpZPIGdvdZ8dxvq6kzxDgA,7981
|
| 527 |
+
pip/_vendor/pygments/style.py,sha256=rSCZWFpg1_DwFMXDU0nEVmAcBHpuQGf9RxvOPPQvKLQ,6420
|
| 528 |
+
pip/_vendor/pygments/styles/__init__.py,sha256=qUk6_1z5KmT8EdJFZYgESmG6P_HJF_2vVrDD7HSCGYY,2042
|
| 529 |
+
pip/_vendor/pygments/styles/__pycache__/__init__.cpython-312.pyc,,
|
| 530 |
+
pip/_vendor/pygments/styles/__pycache__/_mapping.cpython-312.pyc,,
|
| 531 |
+
pip/_vendor/pygments/styles/_mapping.py,sha256=6lovFUE29tz6EsV3XYY4hgozJ7q1JL7cfO3UOlgnS8w,3312
|
| 532 |
+
pip/_vendor/pygments/token.py,sha256=qZwT7LSPy5YBY3JgDjut642CCy7JdQzAfmqD9NmT5j0,6226
|
| 533 |
+
pip/_vendor/pygments/unistring.py,sha256=p5c1i-HhoIhWemy9CUsaN9o39oomYHNxXll0Xfw6tEA,63208
|
| 534 |
+
pip/_vendor/pygments/util.py,sha256=2tj2nS1X9_OpcuSjf8dOET2bDVZhs8cEKd_uT6-Fgg8,10031
|
| 535 |
+
pip/_vendor/pyproject_hooks/__init__.py,sha256=kCehmy0UaBa9oVMD7ZIZrnswfnP3LXZ5lvnNJAL5JBM,491
|
| 536 |
+
pip/_vendor/pyproject_hooks/__pycache__/__init__.cpython-312.pyc,,
|
| 537 |
+
pip/_vendor/pyproject_hooks/__pycache__/_compat.cpython-312.pyc,,
|
| 538 |
+
pip/_vendor/pyproject_hooks/__pycache__/_impl.cpython-312.pyc,,
|
| 539 |
+
pip/_vendor/pyproject_hooks/_compat.py,sha256=by6evrYnqkisiM-MQcvOKs5bgDMzlOSgZqRHNqf04zE,138
|
| 540 |
+
pip/_vendor/pyproject_hooks/_impl.py,sha256=61GJxzQip0IInhuO69ZI5GbNQ82XEDUB_1Gg5_KtUoc,11920
|
| 541 |
+
pip/_vendor/pyproject_hooks/_in_process/__init__.py,sha256=9gQATptbFkelkIy0OfWFEACzqxXJMQDWCH9rBOAZVwQ,546
|
| 542 |
+
pip/_vendor/pyproject_hooks/_in_process/__pycache__/__init__.cpython-312.pyc,,
|
| 543 |
+
pip/_vendor/pyproject_hooks/_in_process/__pycache__/_in_process.cpython-312.pyc,,
|
| 544 |
+
pip/_vendor/pyproject_hooks/_in_process/_in_process.py,sha256=m2b34c917IW5o-Q_6TYIHlsK9lSUlNiyrITTUH_zwew,10927
|
| 545 |
+
pip/_vendor/requests/__init__.py,sha256=HlB_HzhrzGtfD_aaYUwUh1zWXLZ75_YCLyit75d0Vz8,5057
|
| 546 |
+
pip/_vendor/requests/__pycache__/__init__.cpython-312.pyc,,
|
| 547 |
+
pip/_vendor/requests/__pycache__/__version__.cpython-312.pyc,,
|
| 548 |
+
pip/_vendor/requests/__pycache__/_internal_utils.cpython-312.pyc,,
|
| 549 |
+
pip/_vendor/requests/__pycache__/adapters.cpython-312.pyc,,
|
| 550 |
+
pip/_vendor/requests/__pycache__/api.cpython-312.pyc,,
|
| 551 |
+
pip/_vendor/requests/__pycache__/auth.cpython-312.pyc,,
|
| 552 |
+
pip/_vendor/requests/__pycache__/certs.cpython-312.pyc,,
|
| 553 |
+
pip/_vendor/requests/__pycache__/compat.cpython-312.pyc,,
|
| 554 |
+
pip/_vendor/requests/__pycache__/cookies.cpython-312.pyc,,
|
| 555 |
+
pip/_vendor/requests/__pycache__/exceptions.cpython-312.pyc,,
|
| 556 |
+
pip/_vendor/requests/__pycache__/help.cpython-312.pyc,,
|
| 557 |
+
pip/_vendor/requests/__pycache__/hooks.cpython-312.pyc,,
|
| 558 |
+
pip/_vendor/requests/__pycache__/models.cpython-312.pyc,,
|
| 559 |
+
pip/_vendor/requests/__pycache__/packages.cpython-312.pyc,,
|
| 560 |
+
pip/_vendor/requests/__pycache__/sessions.cpython-312.pyc,,
|
| 561 |
+
pip/_vendor/requests/__pycache__/status_codes.cpython-312.pyc,,
|
| 562 |
+
pip/_vendor/requests/__pycache__/structures.cpython-312.pyc,,
|
| 563 |
+
pip/_vendor/requests/__pycache__/utils.cpython-312.pyc,,
|
| 564 |
+
pip/_vendor/requests/__version__.py,sha256=FVfglgZmNQnmYPXpOohDU58F5EUb_-VnSTaAesS187g,435
|
| 565 |
+
pip/_vendor/requests/_internal_utils.py,sha256=nMQymr4hs32TqVo5AbCrmcJEhvPUh7xXlluyqwslLiQ,1495
|
| 566 |
+
pip/_vendor/requests/adapters.py,sha256=J7VeVxKBvawbtlX2DERVo05J9BXTcWYLMHNd1Baa-bk,27607
|
| 567 |
+
pip/_vendor/requests/api.py,sha256=_Zb9Oa7tzVIizTKwFrPjDEY9ejtm_OnSRERnADxGsQs,6449
|
| 568 |
+
pip/_vendor/requests/auth.py,sha256=kF75tqnLctZ9Mf_hm9TZIj4cQWnN5uxRz8oWsx5wmR0,10186
|
| 569 |
+
pip/_vendor/requests/certs.py,sha256=PVPooB0jP5hkZEULSCwC074532UFbR2Ptgu0I5zwmCs,575
|
| 570 |
+
pip/_vendor/requests/compat.py,sha256=Mo9f9xZpefod8Zm-n9_StJcVTmwSukXR2p3IQyyVXvU,1485
|
| 571 |
+
pip/_vendor/requests/cookies.py,sha256=bNi-iqEj4NPZ00-ob-rHvzkvObzN3lEpgw3g6paS3Xw,18590
|
| 572 |
+
pip/_vendor/requests/exceptions.py,sha256=D1wqzYWne1mS2rU43tP9CeN1G7QAy7eqL9o1god6Ejw,4272
|
| 573 |
+
pip/_vendor/requests/help.py,sha256=hRKaf9u0G7fdwrqMHtF3oG16RKktRf6KiwtSq2Fo1_0,3813
|
| 574 |
+
pip/_vendor/requests/hooks.py,sha256=CiuysiHA39V5UfcCBXFIx83IrDpuwfN9RcTUgv28ftQ,733
|
| 575 |
+
pip/_vendor/requests/models.py,sha256=x4K4CmH-lC0l2Kb-iPfMN4dRXxHEcbOaEWBL_i09AwI,35483
|
| 576 |
+
pip/_vendor/requests/packages.py,sha256=_ZQDCJTJ8SP3kVWunSqBsRZNPzj2c1WFVqbdr08pz3U,1057
|
| 577 |
+
pip/_vendor/requests/sessions.py,sha256=ykTI8UWGSltOfH07HKollH7kTBGw4WhiBVaQGmckTw4,30495
|
| 578 |
+
pip/_vendor/requests/status_codes.py,sha256=iJUAeA25baTdw-6PfD0eF4qhpINDJRJI-yaMqxs4LEI,4322
|
| 579 |
+
pip/_vendor/requests/structures.py,sha256=-IbmhVz06S-5aPSZuUthZ6-6D9XOjRuTXHOabY041XM,2912
|
| 580 |
+
pip/_vendor/requests/utils.py,sha256=L79vnFbzJ3SFLKtJwpoWe41Tozi3RlZv94pY1TFIyow,33631
|
| 581 |
+
pip/_vendor/resolvelib/__init__.py,sha256=h509TdEcpb5-44JonaU3ex2TM15GVBLjM9CNCPwnTTs,537
|
| 582 |
+
pip/_vendor/resolvelib/__pycache__/__init__.cpython-312.pyc,,
|
| 583 |
+
pip/_vendor/resolvelib/__pycache__/providers.cpython-312.pyc,,
|
| 584 |
+
pip/_vendor/resolvelib/__pycache__/reporters.cpython-312.pyc,,
|
| 585 |
+
pip/_vendor/resolvelib/__pycache__/resolvers.cpython-312.pyc,,
|
| 586 |
+
pip/_vendor/resolvelib/__pycache__/structs.cpython-312.pyc,,
|
| 587 |
+
pip/_vendor/resolvelib/compat/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 588 |
+
pip/_vendor/resolvelib/compat/__pycache__/__init__.cpython-312.pyc,,
|
| 589 |
+
pip/_vendor/resolvelib/compat/__pycache__/collections_abc.cpython-312.pyc,,
|
| 590 |
+
pip/_vendor/resolvelib/compat/collections_abc.py,sha256=uy8xUZ-NDEw916tugUXm8HgwCGiMO0f-RcdnpkfXfOs,156
|
| 591 |
+
pip/_vendor/resolvelib/providers.py,sha256=fuuvVrCetu5gsxPB43ERyjfO8aReS3rFQHpDgiItbs4,5871
|
| 592 |
+
pip/_vendor/resolvelib/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 593 |
+
pip/_vendor/resolvelib/reporters.py,sha256=TSbRmWzTc26w0ggsV1bxVpeWDB8QNIre6twYl7GIZBE,1601
|
| 594 |
+
pip/_vendor/resolvelib/resolvers.py,sha256=G8rsLZSq64g5VmIq-lB7UcIJ1gjAxIQJmTF4REZleQ0,20511
|
| 595 |
+
pip/_vendor/resolvelib/structs.py,sha256=0_1_XO8z_CLhegP3Vpf9VJ3zJcfLm0NOHRM-i0Ykz3o,4963
|
| 596 |
+
pip/_vendor/rich/__init__.py,sha256=dRxjIL-SbFVY0q3IjSMrfgBTHrm1LZDgLOygVBwiYZc,6090
|
| 597 |
+
pip/_vendor/rich/__main__.py,sha256=eO7Cq8JnrgG8zVoeImiAs92q3hXNMIfp0w5lMsO7Q2Y,8477
|
| 598 |
+
pip/_vendor/rich/__pycache__/__init__.cpython-312.pyc,,
|
| 599 |
+
pip/_vendor/rich/__pycache__/__main__.cpython-312.pyc,,
|
| 600 |
+
pip/_vendor/rich/__pycache__/_cell_widths.cpython-312.pyc,,
|
| 601 |
+
pip/_vendor/rich/__pycache__/_emoji_codes.cpython-312.pyc,,
|
| 602 |
+
pip/_vendor/rich/__pycache__/_emoji_replace.cpython-312.pyc,,
|
| 603 |
+
pip/_vendor/rich/__pycache__/_export_format.cpython-312.pyc,,
|
| 604 |
+
pip/_vendor/rich/__pycache__/_extension.cpython-312.pyc,,
|
| 605 |
+
pip/_vendor/rich/__pycache__/_fileno.cpython-312.pyc,,
|
| 606 |
+
pip/_vendor/rich/__pycache__/_inspect.cpython-312.pyc,,
|
| 607 |
+
pip/_vendor/rich/__pycache__/_log_render.cpython-312.pyc,,
|
| 608 |
+
pip/_vendor/rich/__pycache__/_loop.cpython-312.pyc,,
|
| 609 |
+
pip/_vendor/rich/__pycache__/_null_file.cpython-312.pyc,,
|
| 610 |
+
pip/_vendor/rich/__pycache__/_palettes.cpython-312.pyc,,
|
| 611 |
+
pip/_vendor/rich/__pycache__/_pick.cpython-312.pyc,,
|
| 612 |
+
pip/_vendor/rich/__pycache__/_ratio.cpython-312.pyc,,
|
| 613 |
+
pip/_vendor/rich/__pycache__/_spinners.cpython-312.pyc,,
|
| 614 |
+
pip/_vendor/rich/__pycache__/_stack.cpython-312.pyc,,
|
| 615 |
+
pip/_vendor/rich/__pycache__/_timer.cpython-312.pyc,,
|
| 616 |
+
pip/_vendor/rich/__pycache__/_win32_console.cpython-312.pyc,,
|
| 617 |
+
pip/_vendor/rich/__pycache__/_windows.cpython-312.pyc,,
|
| 618 |
+
pip/_vendor/rich/__pycache__/_windows_renderer.cpython-312.pyc,,
|
| 619 |
+
pip/_vendor/rich/__pycache__/_wrap.cpython-312.pyc,,
|
| 620 |
+
pip/_vendor/rich/__pycache__/abc.cpython-312.pyc,,
|
| 621 |
+
pip/_vendor/rich/__pycache__/align.cpython-312.pyc,,
|
| 622 |
+
pip/_vendor/rich/__pycache__/ansi.cpython-312.pyc,,
|
| 623 |
+
pip/_vendor/rich/__pycache__/bar.cpython-312.pyc,,
|
| 624 |
+
pip/_vendor/rich/__pycache__/box.cpython-312.pyc,,
|
| 625 |
+
pip/_vendor/rich/__pycache__/cells.cpython-312.pyc,,
|
| 626 |
+
pip/_vendor/rich/__pycache__/color.cpython-312.pyc,,
|
| 627 |
+
pip/_vendor/rich/__pycache__/color_triplet.cpython-312.pyc,,
|
| 628 |
+
pip/_vendor/rich/__pycache__/columns.cpython-312.pyc,,
|
| 629 |
+
pip/_vendor/rich/__pycache__/console.cpython-312.pyc,,
|
| 630 |
+
pip/_vendor/rich/__pycache__/constrain.cpython-312.pyc,,
|
| 631 |
+
pip/_vendor/rich/__pycache__/containers.cpython-312.pyc,,
|
| 632 |
+
pip/_vendor/rich/__pycache__/control.cpython-312.pyc,,
|
| 633 |
+
pip/_vendor/rich/__pycache__/default_styles.cpython-312.pyc,,
|
| 634 |
+
pip/_vendor/rich/__pycache__/diagnose.cpython-312.pyc,,
|
| 635 |
+
pip/_vendor/rich/__pycache__/emoji.cpython-312.pyc,,
|
| 636 |
+
pip/_vendor/rich/__pycache__/errors.cpython-312.pyc,,
|
| 637 |
+
pip/_vendor/rich/__pycache__/file_proxy.cpython-312.pyc,,
|
| 638 |
+
pip/_vendor/rich/__pycache__/filesize.cpython-312.pyc,,
|
| 639 |
+
pip/_vendor/rich/__pycache__/highlighter.cpython-312.pyc,,
|
| 640 |
+
pip/_vendor/rich/__pycache__/json.cpython-312.pyc,,
|
| 641 |
+
pip/_vendor/rich/__pycache__/jupyter.cpython-312.pyc,,
|
| 642 |
+
pip/_vendor/rich/__pycache__/layout.cpython-312.pyc,,
|
| 643 |
+
pip/_vendor/rich/__pycache__/live.cpython-312.pyc,,
|
| 644 |
+
pip/_vendor/rich/__pycache__/live_render.cpython-312.pyc,,
|
| 645 |
+
pip/_vendor/rich/__pycache__/logging.cpython-312.pyc,,
|
| 646 |
+
pip/_vendor/rich/__pycache__/markup.cpython-312.pyc,,
|
| 647 |
+
pip/_vendor/rich/__pycache__/measure.cpython-312.pyc,,
|
| 648 |
+
pip/_vendor/rich/__pycache__/padding.cpython-312.pyc,,
|
| 649 |
+
pip/_vendor/rich/__pycache__/pager.cpython-312.pyc,,
|
| 650 |
+
pip/_vendor/rich/__pycache__/palette.cpython-312.pyc,,
|
| 651 |
+
pip/_vendor/rich/__pycache__/panel.cpython-312.pyc,,
|
| 652 |
+
pip/_vendor/rich/__pycache__/pretty.cpython-312.pyc,,
|
| 653 |
+
pip/_vendor/rich/__pycache__/progress.cpython-312.pyc,,
|
| 654 |
+
pip/_vendor/rich/__pycache__/progress_bar.cpython-312.pyc,,
|
| 655 |
+
pip/_vendor/rich/__pycache__/prompt.cpython-312.pyc,,
|
| 656 |
+
pip/_vendor/rich/__pycache__/protocol.cpython-312.pyc,,
|
| 657 |
+
pip/_vendor/rich/__pycache__/region.cpython-312.pyc,,
|
| 658 |
+
pip/_vendor/rich/__pycache__/repr.cpython-312.pyc,,
|
| 659 |
+
pip/_vendor/rich/__pycache__/rule.cpython-312.pyc,,
|
| 660 |
+
pip/_vendor/rich/__pycache__/scope.cpython-312.pyc,,
|
| 661 |
+
pip/_vendor/rich/__pycache__/screen.cpython-312.pyc,,
|
| 662 |
+
pip/_vendor/rich/__pycache__/segment.cpython-312.pyc,,
|
| 663 |
+
pip/_vendor/rich/__pycache__/spinner.cpython-312.pyc,,
|
| 664 |
+
pip/_vendor/rich/__pycache__/status.cpython-312.pyc,,
|
| 665 |
+
pip/_vendor/rich/__pycache__/style.cpython-312.pyc,,
|
| 666 |
+
pip/_vendor/rich/__pycache__/styled.cpython-312.pyc,,
|
| 667 |
+
pip/_vendor/rich/__pycache__/syntax.cpython-312.pyc,,
|
| 668 |
+
pip/_vendor/rich/__pycache__/table.cpython-312.pyc,,
|
| 669 |
+
pip/_vendor/rich/__pycache__/terminal_theme.cpython-312.pyc,,
|
| 670 |
+
pip/_vendor/rich/__pycache__/text.cpython-312.pyc,,
|
| 671 |
+
pip/_vendor/rich/__pycache__/theme.cpython-312.pyc,,
|
| 672 |
+
pip/_vendor/rich/__pycache__/themes.cpython-312.pyc,,
|
| 673 |
+
pip/_vendor/rich/__pycache__/traceback.cpython-312.pyc,,
|
| 674 |
+
pip/_vendor/rich/__pycache__/tree.cpython-312.pyc,,
|
| 675 |
+
pip/_vendor/rich/_cell_widths.py,sha256=fbmeyetEdHjzE_Vx2l1uK7tnPOhMs2X1lJfO3vsKDpA,10209
|
| 676 |
+
pip/_vendor/rich/_emoji_codes.py,sha256=hu1VL9nbVdppJrVoijVshRlcRRe_v3dju3Mmd2sKZdY,140235
|
| 677 |
+
pip/_vendor/rich/_emoji_replace.py,sha256=n-kcetsEUx2ZUmhQrfeMNc-teeGhpuSQ5F8VPBsyvDo,1064
|
| 678 |
+
pip/_vendor/rich/_export_format.py,sha256=RI08pSrm5tBSzPMvnbTqbD9WIalaOoN5d4M1RTmLq1Y,2128
|
| 679 |
+
pip/_vendor/rich/_extension.py,sha256=Xt47QacCKwYruzjDi-gOBq724JReDj9Cm9xUi5fr-34,265
|
| 680 |
+
pip/_vendor/rich/_fileno.py,sha256=HWZxP5C2ajMbHryvAQZseflVfQoGzsKOHzKGsLD8ynQ,799
|
| 681 |
+
pip/_vendor/rich/_inspect.py,sha256=oZJGw31e64dwXSCmrDnvZbwVb1ZKhWfU8wI3VWohjJk,9695
|
| 682 |
+
pip/_vendor/rich/_log_render.py,sha256=1ByI0PA1ZpxZY3CGJOK54hjlq4X-Bz_boIjIqCd8Kns,3225
|
| 683 |
+
pip/_vendor/rich/_loop.py,sha256=hV_6CLdoPm0va22Wpw4zKqM0RYsz3TZxXj0PoS-9eDQ,1236
|
| 684 |
+
pip/_vendor/rich/_null_file.py,sha256=tGSXk_v-IZmbj1GAzHit8A3kYIQMiCpVsCFfsC-_KJ4,1387
|
| 685 |
+
pip/_vendor/rich/_palettes.py,sha256=cdev1JQKZ0JvlguV9ipHgznTdnvlIzUFDBb0It2PzjI,7063
|
| 686 |
+
pip/_vendor/rich/_pick.py,sha256=evDt8QN4lF5CiwrUIXlOJCntitBCOsI3ZLPEIAVRLJU,423
|
| 687 |
+
pip/_vendor/rich/_ratio.py,sha256=Zt58apszI6hAAcXPpgdWKpu3c31UBWebOeR4mbyptvU,5471
|
| 688 |
+
pip/_vendor/rich/_spinners.py,sha256=U2r1_g_1zSjsjiUdAESc2iAMc3i4ri_S8PYP6kQ5z1I,19919
|
| 689 |
+
pip/_vendor/rich/_stack.py,sha256=-C8OK7rxn3sIUdVwxZBBpeHhIzX0eI-VM3MemYfaXm0,351
|
| 690 |
+
pip/_vendor/rich/_timer.py,sha256=zelxbT6oPFZnNrwWPpc1ktUeAT-Vc4fuFcRZLQGLtMI,417
|
| 691 |
+
pip/_vendor/rich/_win32_console.py,sha256=P0vxI2fcndym1UU1S37XAzQzQnkyY7YqAKmxm24_gug,22820
|
| 692 |
+
pip/_vendor/rich/_windows.py,sha256=aBwaD_S56SbgopIvayVmpk0Y28uwY2C5Bab1wl3Bp-I,1925
|
| 693 |
+
pip/_vendor/rich/_windows_renderer.py,sha256=t74ZL3xuDCP3nmTp9pH1L5LiI2cakJuQRQleHCJerlk,2783
|
| 694 |
+
pip/_vendor/rich/_wrap.py,sha256=FlSsom5EX0LVkA3KWy34yHnCfLtqX-ZIepXKh-70rpc,3404
|
| 695 |
+
pip/_vendor/rich/abc.py,sha256=ON-E-ZqSSheZ88VrKX2M3PXpFbGEUUZPMa_Af0l-4f0,890
|
| 696 |
+
pip/_vendor/rich/align.py,sha256=sCUkisXkQfoq-IQPyBELfJ8l7LihZJX3HbH8K7Cie-M,10368
|
| 697 |
+
pip/_vendor/rich/ansi.py,sha256=iD6532QYqnBm6hADulKjrV8l8kFJ-9fEVooHJHH3hMg,6906
|
| 698 |
+
pip/_vendor/rich/bar.py,sha256=ldbVHOzKJOnflVNuv1xS7g6dLX2E3wMnXkdPbpzJTcs,3263
|
| 699 |
+
pip/_vendor/rich/box.py,sha256=nr5fYIUghB_iUCEq6y0Z3LlCT8gFPDrzN9u2kn7tJl4,10831
|
| 700 |
+
pip/_vendor/rich/cells.py,sha256=aMmGK4BjXhgE6_JF1ZEGmW3O7mKkE8g84vUnj4Et4To,4780
|
| 701 |
+
pip/_vendor/rich/color.py,sha256=bCRATVdRe5IClJ6Hl62de2PKQ_U4i2MZ4ugjUEg7Tao,18223
|
| 702 |
+
pip/_vendor/rich/color_triplet.py,sha256=3lhQkdJbvWPoLDO-AnYImAWmJvV5dlgYNCVZ97ORaN4,1054
|
| 703 |
+
pip/_vendor/rich/columns.py,sha256=HUX0KcMm9dsKNi11fTbiM_h2iDtl8ySCaVcxlalEzq8,7131
|
| 704 |
+
pip/_vendor/rich/console.py,sha256=deFZIubq2M9A2MCsKFAsFQlWDvcOMsGuUA07QkOaHIw,99173
|
| 705 |
+
pip/_vendor/rich/constrain.py,sha256=1VIPuC8AgtKWrcncQrjBdYqA3JVWysu6jZo1rrh7c7Q,1288
|
| 706 |
+
pip/_vendor/rich/containers.py,sha256=c_56TxcedGYqDepHBMTuZdUIijitAQgnox-Qde0Z1qo,5502
|
| 707 |
+
pip/_vendor/rich/control.py,sha256=DSkHTUQLorfSERAKE_oTAEUFefZnZp4bQb4q8rHbKws,6630
|
| 708 |
+
pip/_vendor/rich/default_styles.py,sha256=-Fe318kMVI_IwciK5POpThcO0-9DYJ67TZAN6DlmlmM,8082
|
| 709 |
+
pip/_vendor/rich/diagnose.py,sha256=an6uouwhKPAlvQhYpNNpGq9EJysfMIOvvCbO3oSoR24,972
|
| 710 |
+
pip/_vendor/rich/emoji.py,sha256=omTF9asaAnsM4yLY94eR_9dgRRSm1lHUszX20D1yYCQ,2501
|
| 711 |
+
pip/_vendor/rich/errors.py,sha256=5pP3Kc5d4QJ_c0KFsxrfyhjiPVe7J1zOqSFbFAzcV-Y,642
|
| 712 |
+
pip/_vendor/rich/file_proxy.py,sha256=Tl9THMDZ-Pk5Wm8sI1gGg_U5DhusmxD-FZ0fUbcU0W0,1683
|
| 713 |
+
pip/_vendor/rich/filesize.py,sha256=9fTLAPCAwHmBXdRv7KZU194jSgNrRb6Wx7RIoBgqeKY,2508
|
| 714 |
+
pip/_vendor/rich/highlighter.py,sha256=6ZAjUcNhBRajBCo9umFUclyi2xL0-55JL7S0vYGUJu4,9585
|
| 715 |
+
pip/_vendor/rich/json.py,sha256=vVEoKdawoJRjAFayPwXkMBPLy7RSTs-f44wSQDR2nJ0,5031
|
| 716 |
+
pip/_vendor/rich/jupyter.py,sha256=QyoKoE_8IdCbrtiSHp9TsTSNyTHY0FO5whE7jOTd9UE,3252
|
| 717 |
+
pip/_vendor/rich/layout.py,sha256=ajkSFAtEVv9EFTcFs-w4uZfft7nEXhNzL7ZVdgrT5rI,14004
|
| 718 |
+
pip/_vendor/rich/live.py,sha256=vUcnJV2LMSK3sQNaILbm0-_B8BpAeiHfcQMAMLfpRe0,14271
|
| 719 |
+
pip/_vendor/rich/live_render.py,sha256=zJtB471jGziBtEwxc54x12wEQtH4BuQr1SA8v9kU82w,3666
|
| 720 |
+
pip/_vendor/rich/logging.py,sha256=uB-cB-3Q4bmXDLLpbOWkmFviw-Fde39zyMV6tKJ2WHQ,11903
|
| 721 |
+
pip/_vendor/rich/markup.py,sha256=3euGKP5s41NCQwaSjTnJxus5iZMHjxpIM0W6fCxra38,8451
|
| 722 |
+
pip/_vendor/rich/measure.py,sha256=HmrIJX8sWRTHbgh8MxEay_83VkqNW_70s8aKP5ZcYI8,5305
|
| 723 |
+
pip/_vendor/rich/padding.py,sha256=kTFGsdGe0os7tXLnHKpwTI90CXEvrceeZGCshmJy5zw,4970
|
| 724 |
+
pip/_vendor/rich/pager.py,sha256=SO_ETBFKbg3n_AgOzXm41Sv36YxXAyI3_R-KOY2_uSc,828
|
| 725 |
+
pip/_vendor/rich/palette.py,sha256=lInvR1ODDT2f3UZMfL1grq7dY_pDdKHw4bdUgOGaM4Y,3396
|
| 726 |
+
pip/_vendor/rich/panel.py,sha256=2Fd1V7e1kHxlPFIusoHY5T7-Cs0RpkrihgVG9ZVqJ4g,10705
|
| 727 |
+
pip/_vendor/rich/pretty.py,sha256=5oIHP_CGWnHEnD0zMdW5qfGC5kHqIKn7zH_eC4crULE,35848
|
| 728 |
+
pip/_vendor/rich/progress.py,sha256=P02xi7T2Ua3qq17o83bkshe4c0v_45cg8VyTj6US6Vg,59715
|
| 729 |
+
pip/_vendor/rich/progress_bar.py,sha256=L4jw8E6Qb_x-jhOrLVhkuMaPmiAhFIl8jHQbWFrKuR8,8164
|
| 730 |
+
pip/_vendor/rich/prompt.py,sha256=wdOn2X8XTJKnLnlw6PoMY7xG4iUPp3ezt4O5gqvpV-E,11304
|
| 731 |
+
pip/_vendor/rich/protocol.py,sha256=5hHHDDNHckdk8iWH5zEbi-zuIVSF5hbU2jIo47R7lTE,1391
|
| 732 |
+
pip/_vendor/rich/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 733 |
+
pip/_vendor/rich/region.py,sha256=rNT9xZrVZTYIXZC0NYn41CJQwYNbR-KecPOxTgQvB8Y,166
|
| 734 |
+
pip/_vendor/rich/repr.py,sha256=5MZJZmONgC6kud-QW-_m1okXwL2aR6u6y-pUcUCJz28,4431
|
| 735 |
+
pip/_vendor/rich/rule.py,sha256=0fNaS_aERa3UMRc3T5WMpN_sumtDxfaor2y3of1ftBk,4602
|
| 736 |
+
pip/_vendor/rich/scope.py,sha256=TMUU8qo17thyqQCPqjDLYpg_UU1k5qVd-WwiJvnJVas,2843
|
| 737 |
+
pip/_vendor/rich/screen.py,sha256=YoeReESUhx74grqb0mSSb9lghhysWmFHYhsbMVQjXO8,1591
|
| 738 |
+
pip/_vendor/rich/segment.py,sha256=hU1ueeXqI6YeFa08K9DAjlF2QLxcJY9pwZx7RsXavlk,24246
|
| 739 |
+
pip/_vendor/rich/spinner.py,sha256=15koCmF0DQeD8-k28Lpt6X_zJQUlzEhgo_6A6uy47lc,4339
|
| 740 |
+
pip/_vendor/rich/status.py,sha256=kkPph3YeAZBo-X-4wPp8gTqZyU466NLwZBA4PZTTewo,4424
|
| 741 |
+
pip/_vendor/rich/style.py,sha256=3hiocH_4N8vwRm3-8yFWzM7tSwjjEven69XqWasSQwM,27073
|
| 742 |
+
pip/_vendor/rich/styled.py,sha256=eZNnzGrI4ki_54pgY3Oj0T-x3lxdXTYh4_ryDB24wBU,1258
|
| 743 |
+
pip/_vendor/rich/syntax.py,sha256=TnZDuOD4DeHFbkaVEAji1gf8qgAlMU9Boe_GksMGCkk,35475
|
| 744 |
+
pip/_vendor/rich/table.py,sha256=nGEvAZHF4dy1vT9h9Gj9O5qhSQO3ODAxJv0RY1vnIB8,39680
|
| 745 |
+
pip/_vendor/rich/terminal_theme.py,sha256=1j5-ufJfnvlAo5Qsi_ACZiXDmwMXzqgmFByObT9-yJY,3370
|
| 746 |
+
pip/_vendor/rich/text.py,sha256=5rQ3zvNrg5UZKNLecbh7fiw9v3HeFulNVtRY_CBDjjE,47312
|
| 747 |
+
pip/_vendor/rich/theme.py,sha256=belFJogzA0W0HysQabKaHOc3RWH2ko3fQAJhoN-AFdo,3777
|
| 748 |
+
pip/_vendor/rich/themes.py,sha256=0xgTLozfabebYtcJtDdC5QkX5IVUEaviqDUJJh4YVFk,102
|
| 749 |
+
pip/_vendor/rich/traceback.py,sha256=CUpxYLjQWIb6vQQ6O72X0hvDV6caryGqU6UweHgOyCY,29601
|
| 750 |
+
pip/_vendor/rich/tree.py,sha256=meAOUU6sYnoBEOX2ILrPLY9k5bWrWNQKkaiEFvHinXM,9167
|
| 751 |
+
pip/_vendor/tomli/__init__.py,sha256=JhUwV66DB1g4Hvt1UQCVMdfCu-IgAV8FXmvDU9onxd4,396
|
| 752 |
+
pip/_vendor/tomli/__pycache__/__init__.cpython-312.pyc,,
|
| 753 |
+
pip/_vendor/tomli/__pycache__/_parser.cpython-312.pyc,,
|
| 754 |
+
pip/_vendor/tomli/__pycache__/_re.cpython-312.pyc,,
|
| 755 |
+
pip/_vendor/tomli/__pycache__/_types.cpython-312.pyc,,
|
| 756 |
+
pip/_vendor/tomli/_parser.py,sha256=g9-ENaALS-B8dokYpCuzUFalWlog7T-SIYMjLZSWrtM,22633
|
| 757 |
+
pip/_vendor/tomli/_re.py,sha256=dbjg5ChZT23Ka9z9DHOXfdtSpPwUfdgMXnj8NOoly-w,2943
|
| 758 |
+
pip/_vendor/tomli/_types.py,sha256=-GTG2VUqkpxwMqzmVO4F7ybKddIbAnuAHXfmWQcTi3Q,254
|
| 759 |
+
pip/_vendor/tomli/py.typed,sha256=8PjyZ1aVoQpRVvt71muvuq5qE-jTFZkK-GLHkhdebmc,26
|
| 760 |
+
pip/_vendor/truststore/__init__.py,sha256=WIDeyzWm7EVX44g354M25vpRXbeY1lsPH6EmUJUcq4o,1264
|
| 761 |
+
pip/_vendor/truststore/__pycache__/__init__.cpython-312.pyc,,
|
| 762 |
+
pip/_vendor/truststore/__pycache__/_api.cpython-312.pyc,,
|
| 763 |
+
pip/_vendor/truststore/__pycache__/_macos.cpython-312.pyc,,
|
| 764 |
+
pip/_vendor/truststore/__pycache__/_openssl.cpython-312.pyc,,
|
| 765 |
+
pip/_vendor/truststore/__pycache__/_ssl_constants.cpython-312.pyc,,
|
| 766 |
+
pip/_vendor/truststore/__pycache__/_windows.cpython-312.pyc,,
|
| 767 |
+
pip/_vendor/truststore/_api.py,sha256=GeXRNTlxPZ3kif4kNoh6JY0oE4QRzTGcgXr6l_X_Gk0,10555
|
| 768 |
+
pip/_vendor/truststore/_macos.py,sha256=nZlLkOmszUE0g6ryRwBVGY5COzPyudcsiJtDWarM5LQ,20503
|
| 769 |
+
pip/_vendor/truststore/_openssl.py,sha256=LLUZ7ZGaio-i5dpKKjKCSeSufmn6T8pi9lDcFnvSyq0,2324
|
| 770 |
+
pip/_vendor/truststore/_ssl_constants.py,sha256=NUD4fVKdSD02ri7-db0tnO0VqLP9aHuzmStcW7tAl08,1130
|
| 771 |
+
pip/_vendor/truststore/_windows.py,sha256=rAHyKYD8M7t-bXfG8VgOVa3TpfhVhbt4rZQlO45YuP8,17993
|
| 772 |
+
pip/_vendor/truststore/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 773 |
+
pip/_vendor/typing_extensions.py,sha256=78hFl0HpDY-ylHUVCnWdU5nTHxUP2-S-3wEZk6CQmLk,134499
|
| 774 |
+
pip/_vendor/urllib3/__init__.py,sha256=iXLcYiJySn0GNbWOOZDDApgBL1JgP44EZ8i1760S8Mc,3333
|
| 775 |
+
pip/_vendor/urllib3/__pycache__/__init__.cpython-312.pyc,,
|
| 776 |
+
pip/_vendor/urllib3/__pycache__/_collections.cpython-312.pyc,,
|
| 777 |
+
pip/_vendor/urllib3/__pycache__/_version.cpython-312.pyc,,
|
| 778 |
+
pip/_vendor/urllib3/__pycache__/connection.cpython-312.pyc,,
|
| 779 |
+
pip/_vendor/urllib3/__pycache__/connectionpool.cpython-312.pyc,,
|
| 780 |
+
pip/_vendor/urllib3/__pycache__/exceptions.cpython-312.pyc,,
|
| 781 |
+
pip/_vendor/urllib3/__pycache__/fields.cpython-312.pyc,,
|
| 782 |
+
pip/_vendor/urllib3/__pycache__/filepost.cpython-312.pyc,,
|
| 783 |
+
pip/_vendor/urllib3/__pycache__/poolmanager.cpython-312.pyc,,
|
| 784 |
+
pip/_vendor/urllib3/__pycache__/request.cpython-312.pyc,,
|
| 785 |
+
pip/_vendor/urllib3/__pycache__/response.cpython-312.pyc,,
|
| 786 |
+
pip/_vendor/urllib3/_collections.py,sha256=pyASJJhW7wdOpqJj9QJA8FyGRfr8E8uUUhqUvhF0728,11372
|
| 787 |
+
pip/_vendor/urllib3/_version.py,sha256=t9wGB6ooOTXXgiY66K1m6BZS1CJyXHAU8EoWDTe6Shk,64
|
| 788 |
+
pip/_vendor/urllib3/connection.py,sha256=ttIA909BrbTUzwkqEe_TzZVh4JOOj7g61Ysei2mrwGg,20314
|
| 789 |
+
pip/_vendor/urllib3/connectionpool.py,sha256=e2eiAwNbFNCKxj4bwDKNK-w7HIdSz3OmMxU_TIt-evQ,40408
|
| 790 |
+
pip/_vendor/urllib3/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 791 |
+
pip/_vendor/urllib3/contrib/__pycache__/__init__.cpython-312.pyc,,
|
| 792 |
+
pip/_vendor/urllib3/contrib/__pycache__/_appengine_environ.cpython-312.pyc,,
|
| 793 |
+
pip/_vendor/urllib3/contrib/__pycache__/appengine.cpython-312.pyc,,
|
| 794 |
+
pip/_vendor/urllib3/contrib/__pycache__/ntlmpool.cpython-312.pyc,,
|
| 795 |
+
pip/_vendor/urllib3/contrib/__pycache__/pyopenssl.cpython-312.pyc,,
|
| 796 |
+
pip/_vendor/urllib3/contrib/__pycache__/securetransport.cpython-312.pyc,,
|
| 797 |
+
pip/_vendor/urllib3/contrib/__pycache__/socks.cpython-312.pyc,,
|
| 798 |
+
pip/_vendor/urllib3/contrib/_appengine_environ.py,sha256=bDbyOEhW2CKLJcQqAKAyrEHN-aklsyHFKq6vF8ZFsmk,957
|
| 799 |
+
pip/_vendor/urllib3/contrib/_securetransport/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 800 |
+
pip/_vendor/urllib3/contrib/_securetransport/__pycache__/__init__.cpython-312.pyc,,
|
| 801 |
+
pip/_vendor/urllib3/contrib/_securetransport/__pycache__/bindings.cpython-312.pyc,,
|
| 802 |
+
pip/_vendor/urllib3/contrib/_securetransport/__pycache__/low_level.cpython-312.pyc,,
|
| 803 |
+
pip/_vendor/urllib3/contrib/_securetransport/bindings.py,sha256=4Xk64qIkPBt09A5q-RIFUuDhNc9mXilVapm7WnYnzRw,17632
|
| 804 |
+
pip/_vendor/urllib3/contrib/_securetransport/low_level.py,sha256=B2JBB2_NRP02xK6DCa1Pa9IuxrPwxzDzZbixQkb7U9M,13922
|
| 805 |
+
pip/_vendor/urllib3/contrib/appengine.py,sha256=VR68eAVE137lxTgjBDwCna5UiBZTOKa01Aj_-5BaCz4,11036
|
| 806 |
+
pip/_vendor/urllib3/contrib/ntlmpool.py,sha256=NlfkW7WMdW8ziqudopjHoW299og1BTWi0IeIibquFwk,4528
|
| 807 |
+
pip/_vendor/urllib3/contrib/pyopenssl.py,sha256=hDJh4MhyY_p-oKlFcYcQaVQRDv6GMmBGuW9yjxyeejM,17081
|
| 808 |
+
pip/_vendor/urllib3/contrib/securetransport.py,sha256=Fef1IIUUFHqpevzXiDPbIGkDKchY2FVKeVeLGR1Qq3g,34446
|
| 809 |
+
pip/_vendor/urllib3/contrib/socks.py,sha256=aRi9eWXo9ZEb95XUxef4Z21CFlnnjbEiAo9HOseoMt4,7097
|
| 810 |
+
pip/_vendor/urllib3/exceptions.py,sha256=0Mnno3KHTNfXRfY7638NufOPkUb6mXOm-Lqj-4x2w8A,8217
|
| 811 |
+
pip/_vendor/urllib3/fields.py,sha256=kvLDCg_JmH1lLjUUEY_FLS8UhY7hBvDPuVETbY8mdrM,8579
|
| 812 |
+
pip/_vendor/urllib3/filepost.py,sha256=5b_qqgRHVlL7uLtdAYBzBh-GHmU5AfJVt_2N0XS3PeY,2440
|
| 813 |
+
pip/_vendor/urllib3/packages/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 814 |
+
pip/_vendor/urllib3/packages/__pycache__/__init__.cpython-312.pyc,,
|
| 815 |
+
pip/_vendor/urllib3/packages/__pycache__/six.cpython-312.pyc,,
|
| 816 |
+
pip/_vendor/urllib3/packages/backports/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
| 817 |
+
pip/_vendor/urllib3/packages/backports/__pycache__/__init__.cpython-312.pyc,,
|
| 818 |
+
pip/_vendor/urllib3/packages/backports/__pycache__/makefile.cpython-312.pyc,,
|
| 819 |
+
pip/_vendor/urllib3/packages/backports/__pycache__/weakref_finalize.cpython-312.pyc,,
|
| 820 |
+
pip/_vendor/urllib3/packages/backports/makefile.py,sha256=nbzt3i0agPVP07jqqgjhaYjMmuAi_W5E0EywZivVO8E,1417
|
| 821 |
+
pip/_vendor/urllib3/packages/backports/weakref_finalize.py,sha256=tRCal5OAhNSRyb0DhHp-38AtIlCsRP8BxF3NX-6rqIA,5343
|
| 822 |
+
pip/_vendor/urllib3/packages/six.py,sha256=b9LM0wBXv7E7SrbCjAm4wwN-hrH-iNxv18LgWNMMKPo,34665
|
| 823 |
+
pip/_vendor/urllib3/poolmanager.py,sha256=aWyhXRtNO4JUnCSVVqKTKQd8EXTvUm1VN9pgs2bcONo,19990
|
| 824 |
+
pip/_vendor/urllib3/request.py,sha256=YTWFNr7QIwh7E1W9dde9LM77v2VWTJ5V78XuTTw7D1A,6691
|
| 825 |
+
pip/_vendor/urllib3/response.py,sha256=fmDJAFkG71uFTn-sVSTh2Iw0WmcXQYqkbRjihvwBjU8,30641
|
| 826 |
+
pip/_vendor/urllib3/util/__init__.py,sha256=JEmSmmqqLyaw8P51gUImZh8Gwg9i1zSe-DoqAitn2nc,1155
|
| 827 |
+
pip/_vendor/urllib3/util/__pycache__/__init__.cpython-312.pyc,,
|
| 828 |
+
pip/_vendor/urllib3/util/__pycache__/connection.cpython-312.pyc,,
|
| 829 |
+
pip/_vendor/urllib3/util/__pycache__/proxy.cpython-312.pyc,,
|
| 830 |
+
pip/_vendor/urllib3/util/__pycache__/queue.cpython-312.pyc,,
|
| 831 |
+
pip/_vendor/urllib3/util/__pycache__/request.cpython-312.pyc,,
|
| 832 |
+
pip/_vendor/urllib3/util/__pycache__/response.cpython-312.pyc,,
|
| 833 |
+
pip/_vendor/urllib3/util/__pycache__/retry.cpython-312.pyc,,
|
| 834 |
+
pip/_vendor/urllib3/util/__pycache__/ssl_.cpython-312.pyc,,
|
| 835 |
+
pip/_vendor/urllib3/util/__pycache__/ssl_match_hostname.cpython-312.pyc,,
|
| 836 |
+
pip/_vendor/urllib3/util/__pycache__/ssltransport.cpython-312.pyc,,
|
| 837 |
+
pip/_vendor/urllib3/util/__pycache__/timeout.cpython-312.pyc,,
|
| 838 |
+
pip/_vendor/urllib3/util/__pycache__/url.cpython-312.pyc,,
|
| 839 |
+
pip/_vendor/urllib3/util/__pycache__/wait.cpython-312.pyc,,
|
| 840 |
+
pip/_vendor/urllib3/util/connection.py,sha256=5Lx2B1PW29KxBn2T0xkN1CBgRBa3gGVJBKoQoRogEVk,4901
|
| 841 |
+
pip/_vendor/urllib3/util/proxy.py,sha256=zUvPPCJrp6dOF0N4GAVbOcl6o-4uXKSrGiTkkr5vUS4,1605
|
| 842 |
+
pip/_vendor/urllib3/util/queue.py,sha256=nRgX8_eX-_VkvxoX096QWoz8Ps0QHUAExILCY_7PncM,498
|
| 843 |
+
pip/_vendor/urllib3/util/request.py,sha256=C0OUt2tcU6LRiQJ7YYNP9GvPrSvl7ziIBekQ-5nlBZk,3997
|
| 844 |
+
pip/_vendor/urllib3/util/response.py,sha256=GJpg3Egi9qaJXRwBh5wv-MNuRWan5BIu40oReoxWP28,3510
|
| 845 |
+
pip/_vendor/urllib3/util/retry.py,sha256=6ENvOZ8PBDzh8kgixpql9lIrb2dxH-k7ZmBanJF2Ng4,22050
|
| 846 |
+
pip/_vendor/urllib3/util/ssl_.py,sha256=QDuuTxPSCj1rYtZ4xpD7Ux-r20TD50aHyqKyhQ7Bq4A,17460
|
| 847 |
+
pip/_vendor/urllib3/util/ssl_match_hostname.py,sha256=Ir4cZVEjmAk8gUAIHWSi7wtOO83UCYABY2xFD1Ql_WA,5758
|
| 848 |
+
pip/_vendor/urllib3/util/ssltransport.py,sha256=NA-u5rMTrDFDFC8QzRKUEKMG0561hOD4qBTr3Z4pv6E,6895
|
| 849 |
+
pip/_vendor/urllib3/util/timeout.py,sha256=cwq4dMk87mJHSBktK1miYJ-85G-3T3RmT20v7SFCpno,10168
|
| 850 |
+
pip/_vendor/urllib3/util/url.py,sha256=lCAE7M5myA8EDdW0sJuyyZhVB9K_j38ljWhHAnFaWoE,14296
|
| 851 |
+
pip/_vendor/urllib3/util/wait.py,sha256=fOX0_faozG2P7iVojQoE1mbydweNyTcm-hXEfFrTtLI,5403
|
| 852 |
+
pip/_vendor/vendor.txt,sha256=43152uDtpsunEE29vmLqqKZUosdrbvzIFkzscLB55Cg,332
|
| 853 |
+
pip/py.typed,sha256=EBVvvPRTn_eIpz5e5QztSCdrMX7Qwd7VP93RSoIlZ2I,286
|
venv/lib/python3.12/site-packages/pip-24.3.1.dist-info/REQUESTED
ADDED
|
File without changes
|
venv/lib/python3.12/site-packages/pip-24.3.1.dist-info/WHEEL
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
Wheel-Version: 1.0
|
| 2 |
+
Generator: setuptools (75.2.0)
|
| 3 |
+
Root-Is-Purelib: true
|
| 4 |
+
Tag: py3-none-any
|
| 5 |
+
|
venv/lib/python3.12/site-packages/pip-24.3.1.dist-info/entry_points.txt
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[console_scripts]
|
| 2 |
+
pip = pip._internal.cli.main:main
|
| 3 |
+
pip3 = pip._internal.cli.main:main
|
venv/lib/python3.12/site-packages/pip-24.3.1.dist-info/top_level.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
pip
|
venv/lib/python3.12/site-packages/pip/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from typing import List, Optional
|
| 2 |
+
|
| 3 |
+
__version__ = "24.3.1"
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def main(args: Optional[List[str]] = None) -> int:
|
| 7 |
+
"""This is an internal API only meant for use by pip's own console scripts.
|
| 8 |
+
|
| 9 |
+
For additional details, see https://github.com/pypa/pip/issues/7498.
|
| 10 |
+
"""
|
| 11 |
+
from pip._internal.utils.entrypoints import _wrapper
|
| 12 |
+
|
| 13 |
+
return _wrapper(args)
|